/*
 * Code for user interface menues
 */

#include <string.h>
#include <assert.h>

#include <spr.h>
#include <debuglog.h>
#include <hw.h>
#include <pwin.h>
#include <display.h>
#include <lang.h>
#include <unicode.h>
#include <config.h>
#include <configPriority.h>
#include <mainthread.h>
#include <keypad.h>

#include <ui.h>
#include <uiSpeedDial.h>
#include <callCtrl.h>
#include <uiTestMain.h>
#include <phonebook.h>
#include <speeddial.h>

#include "zcom_uiModelCapability.h"
/*===========================================================================*/
/*============== Data       =================================================*/
/*===========================================================================*/

typedef struct _ZComInputMethod_
{
    unsigned int ZComInputMethodTypeCurrent;
    unsigned int ZComInputMethodTypeLastInUsed;
} ZComInputMethod;

static ZComInputMethod s_ZComInputMethodState = {};
static void uiMenuSymbolSelectionStart(void);

#define UI_CLEAR_KEY_PRESS_SECONDS 2    // how long to hold key down to clear one line
static struct pollerTask ClearButtonTask;
static UIMenuNode nodeList[200];
// junky 2007-4-24
#define ZCOM_REPAINT_INTERVAL 500 //ms
#define ZCOM_REPAINT_STEP1    1
#define ZCOM_REPAINT_STEP2    2
int RepaintScrollStrStep = ZCOM_REPAINT_STEP1;
//int LastnPos = 0;
char strGlobeSource[UI_MENU_MAXSTR] = {};

static struct uiMenuState
{
    UIMenu MenuStack[UI_MENU_MAXDEPTH]; // nested menus
    int MenuTop;                // index in uiMenuS.MenuStack of visible menu
    Pw_Window MenuWin;          // display context
    int MenuWinMapped;          // if MenuWin is active

    struct pollerTask MenuRefreshTask;     /* updates current menu */
    // junky 2007-4-24
    struct pollerTask zcom_RepaintScrollStrTask; /* reflesh scroll string task */
    int ShowShortcutHints;  // to show digits in front of menu lines
} uiMenuS = {};

//static Pw_Bool uiMenuWinEvents(Pw_Event *ev);
static void uiMenuWinRepaint(Pw_GC *gc);

//by Mike
typedef struct
{
	UIMenu * menu;
	int pbIndex; //index in phone book
} Text_List_PBook_CB_PARAM;

static Text_List_PBook_CB_PARAM _cbPara[200];

static void _PBook_SelectCB(void * arg)
{
	Text_List_PBook_CB_PARAM * para = (Text_List_PBook_CB_PARAM *)arg;
	if(para && para->menu && para->pbIndex>=0)
	{
		//call doneCB here
		if ( para->menu->m.text.doneCB )
		{
			char number[CFG_PHONEBOOK_DIGIT_MAX]={};
			if(PhoneBook_RecordGetNumber(para->pbIndex, number, sizeof(number))==0)
			{
				para->menu->m.text.doneCB(para->menu->m.text.doneCBarg,
					number);
			}
		}
	}
	else
	{
		debugf("[_PBook_SelectCB] Invalid param!\n");
	}
}


/*===========================================================================*/
/*============== Generic Menu Code ==========================================*/
/*===========================================================================*/

//
// Hold * for switch quiet profile and current profile
//
#define ZCOM_HOLD_STAR_KEY_MSECS 2000
#define ZCOM_HOLD_POUND_KEY_MSECS 1500
static struct pollerTask zcom_HoldStarKeyTask;
static struct pollerTask zcom_HoldPoundKeyTask;
static bool zcom_WaitStarKeyReleased = false;
static bool zcom_WaitPoundKeyReleased = false;

extern unsigned int g_ZComInputMethodSequencePromptNeed;

static void zcom_uiHoldPoundKeyTask(struct pollerTask *taskP)
{
    if (zcom_WaitPoundKeyReleased)
    {
        //
        // Enter P2P call
        //
        uiMenuPopAll();
        zcom_uiP2PEntryStart();
    }
}

static void zcom_uiHoldStarKeyTask(struct pollerTask *taskP)
{
    if (zcom_WaitStarKeyReleased)
    {
        //
        // Switch quiet profile and current profile
        //
        zcom_uiProfilesSwitchMeetingAndCurrent();
    }
}

//
// Keypad Lock
//
#define ZCOM_KEYPAD_WAIT_NEXT_KEY_MSECS 1500    // in unit of ms , wait * key to lock the keypad

typedef enum _ZCOM_KEYPAD_STATE_ {
    ZCOM_KEYPAD_UNLOCK,
    ZCOM_KEYPAD_LOCK,
    ZCOM_KEYPAD_UNLOCK_WAIT_NEXT_KEY,
    ZCOM_KEYPAD_LOCK_WAIT_NEXT_KEY
} ZCOM_KEYPAD_STATE;

typedef struct _zcom_uiKeyPadLockState_ {
    ZCOM_KEYPAD_STATE zcom_KeyPadLockState;
    struct pollerTask zcom_AutoKeyPadLockTask;
    struct pollerTask zcom_KeyPadWaitNextKeyTask;

    bool zcom_WaitPowerKeyReleased;
} zcom_uiKeyPadLockState;

static zcom_uiKeyPadLockState zcom_uiKeyPadLockS;

void zcom_StartAutoKeypadLockJob()
{
    int mSec = 0;

    pollerTaskStop(&zcom_uiKeyPadLockS.zcom_AutoKeyPadLockTask);

    switch (zcom_uiPhoneSettingPhoneLockDelayGet()) {
    case ZCOM_UI_PHONESETTING_PHONE_LOCK_DELAY_OFF:
        mSec = 0;
        break;
    case ZCOM_UI_PHONESETTING_PHONE_LOCK_DELAY_15S:
        mSec = 15*1000;
        break;
    case ZCOM_UI_PHONESETTING_PHONE_LOCK_DELAY_30S:
        mSec = 30*1000;
        break;
    case ZCOM_UI_PHONESETTING_PHONE_LOCK_DELAY_60S:
        mSec = 60*1000;
        break;
    case ZCOM_UI_PHONESETTING_PHONE_LOCK_DELAY_120S:
        mSec = 120*1000;
        break;
    default:
        mSec = 0;
    }

    if (mSec > 0) {
        pollerTaskTimeoutRel(
            &zcom_uiKeyPadLockS.zcom_AutoKeyPadLockTask,
            pollerClockMsecToTicks(mSec));
    }
}

//
// Driver keypad lock state function
// Return value: true  --- this key will be addressed
//               false --- this key will not be addressed
//
static bool zcom_uiKeypadLockKeyEvent(int press, int keycode, int keyRepeat)
{
    bool bKeyAddressed = true;

    switch(zcom_uiKeyPadLockS.zcom_KeyPadLockState)
    {
    case ZCOM_KEYPAD_UNLOCK:
        {
            if (press == 1)
            {
                if (uiMenuLevelGet() == 0 && keycode == g_zcomKeypadLockKeyCombination.first_keycode)
                {
                    zcom_uiKeyPadLockS.zcom_KeyPadLockState = ZCOM_KEYPAD_UNLOCK_WAIT_NEXT_KEY;

                    //
                    // Stop auto keypad lock task
                    //
                    pollerTaskStop(&zcom_uiKeyPadLockS.zcom_AutoKeyPadLockTask);

                    //
                    // Schedule wait next task
                    //
                    pollerTaskTimeoutRel(
                        &zcom_uiKeyPadLockS.zcom_KeyPadWaitNextKeyTask,
                        pollerClockMsecToTicks(ZCOM_KEYPAD_WAIT_NEXT_KEY_MSECS));

                }
                else
                {
                    //
                    // Reschedule auto kepad lock task
                    //
                    zcom_StartAutoKeypadLockJob();
                }
            }
        }
        break;

    case ZCOM_KEYPAD_UNLOCK_WAIT_NEXT_KEY:
        {
            if (press == 1)
            {
                if (keycode == g_zcomKeypadLockKeyCombination.next_keycode)
                {
                    zcom_uiKeyPadLockS.zcom_KeyPadLockState = ZCOM_KEYPAD_LOCK;

                    //
                    // Stop wait next key task
                    //
                    pollerTaskStop(&zcom_uiKeyPadLockS.zcom_KeyPadWaitNextKeyTask);

                    //
                    // Stop auto keypad lock task
                    //
                    pollerTaskStop(&zcom_uiKeyPadLockS.zcom_AutoKeyPadLockTask);

                    dispIconSet(uiDisplay(), DISP_ICON_PADLOCK, 1);
                    uiMenuPopAll();
                    uiMenuDialogMesgTimed(
                        LANG_ZCOM_STRING_RES_KeyPad_Lock_Prompt_Title,
                        langGet(LANG_ZCOM_STRING_RES_KeyPad_Lock_Msg));
                }
                else
                {
                    zcom_uiKeyPadLockS.zcom_KeyPadLockState = ZCOM_KEYPAD_UNLOCK;

                    if (g_zcomKeypadLockKeyCombination.next_keycode == KEYSYM_SOFT_POSITIVE)
                    {
                        //
                        // Resume the text of SoftKey to "Talk"
                        //
                        UIMenu *menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];
                        uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Talk);
                        uiMenuWindowRepaint();
                    }

                    //
                    // Stop wait star task
                    //
                    pollerTaskStop(&zcom_uiKeyPadLockS.zcom_KeyPadWaitNextKeyTask);

                    //
                    // Reschedule auto kepad lock task
                    //
                    zcom_StartAutoKeypadLockJob();
                }
            }
        }
        break;

    case ZCOM_KEYPAD_LOCK_WAIT_NEXT_KEY:
        {
            //
            // Block all key event
            //
            bKeyAddressed = false;

            if (press == 1)
            {
                if (keycode == g_zcomKeypadLockKeyCombination.next_keycode)
                {
                    zcom_uiKeyPadLockS.zcom_KeyPadLockState = ZCOM_KEYPAD_UNLOCK;

                    //
                    // Stop wait nexy key task
                    //
                    pollerTaskStop(&zcom_uiKeyPadLockS.zcom_KeyPadWaitNextKeyTask);

                    //
                    // Reschedule auto kepad lock task
                    //
                    zcom_StartAutoKeypadLockJob();

                    dispIconSet(uiDisplay(), DISP_ICON_PADLOCK, 0);
                    uiMenuDialogMesgTimed(
                        LANG_ZCOM_STRING_RES_KeyPad_Lock_Prompt_Title,
                        langGet(LANG_ZCOM_STRING_RES_KeyPad_Unlock_Msg));
                }
            }
        }
        break;

    case ZCOM_KEYPAD_LOCK:
        {
            //
            // Block all key event
            //
            bKeyAddressed = false;

            if (press == 1)
            {
                if (keycode == g_zcomKeypadLockKeyCombination.first_keycode)
                {
                    zcom_uiKeyPadLockS.zcom_KeyPadLockState = ZCOM_KEYPAD_LOCK_WAIT_NEXT_KEY;

                    //
                    // Schedule wait next key task
                    //
                    pollerTaskTimeoutRel(
                        &zcom_uiKeyPadLockS.zcom_KeyPadWaitNextKeyTask,
                        pollerClockMsecToTicks(ZCOM_KEYPAD_WAIT_NEXT_KEY_MSECS));

                    //
                    // Prompt press next key to unlock
                    //
                    uiMenuDialogMesgTimed(
                        LANG_ZCOM_STRING_RES_KeyPad_Lock_Prompt_Title,
                        langGet(LANG_ZCOM_STRING_RES_KeyPad_Lock_Prompt_Wait_Star));
                }
                else
                {
                    //
                    // Prompt unlock
                    //
                    uiMenuDialogMesgTimed(
                        LANG_ZCOM_STRING_RES_KeyPad_Lock_Prompt_Title,
                        langGet(LANG_ZCOM_STRING_RES_KeyPad_Lock_Prompt_Unlock));
                }
            }
        }
        break;

    default:
        break;
    }

    if (zcom_uiKeyPadLockS.zcom_WaitPowerKeyReleased)
    {
        //
        // Do not block any key event when waiting for power key released
        //
        return true;
    }

    return bKeyAddressed;
}

static void zcom_uiKeyPadWaitNextKeyTask(struct pollerTask *taskP)
{
    if (zcom_uiKeyPadLockS.zcom_KeyPadLockState == ZCOM_KEYPAD_UNLOCK_WAIT_NEXT_KEY)
    {
        zcom_uiKeyPadLockS.zcom_KeyPadLockState = ZCOM_KEYPAD_UNLOCK;

        if (g_zcomKeypadLockKeyCombination.next_keycode == KEYSYM_SOFT_POSITIVE)
        {
            //
            // Resume the text of SoftKey to "Talk"
            //
            UIMenu *menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];
            uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Talk);
            uiMenuWindowRepaint();
        }

        //
        // Reschedule auto kepad lock task
        //
        zcom_StartAutoKeypadLockJob();
    }
    else if (zcom_uiKeyPadLockS.zcom_KeyPadLockState == ZCOM_KEYPAD_LOCK_WAIT_NEXT_KEY)
    {
        zcom_uiKeyPadLockS.zcom_KeyPadLockState = ZCOM_KEYPAD_LOCK;

        //
        // Stop auto keypad lock task
        //
        pollerTaskStop(&zcom_uiKeyPadLockS.zcom_AutoKeyPadLockTask);
    }
}

static void zcom_uiAutoKeyPadLockTask(struct pollerTask *taskP)
{
    if (zcom_uiKeyPadLockS.zcom_KeyPadLockState == ZCOM_KEYPAD_UNLOCK)
    {
        if (uiFirmwareIsUpgrading() ||
            zcom_callCtrlHaveIncomingOrOutgoingCall() ||
            (uiMenuLevelGet() != 0))
        {
            //
            // Do not change keypad lock state, and reschedule
            //
            zcom_StartAutoKeypadLockJob();
        }
        else
        {
            zcom_uiKeyPadLockS.zcom_KeyPadLockState = ZCOM_KEYPAD_LOCK;

            //
            // Stop auto keypad lock task
            //
            pollerTaskStop(&zcom_uiKeyPadLockS.zcom_AutoKeyPadLockTask);

            dispIconSet(uiDisplay(), DISP_ICON_PADLOCK, 1);
            uiMenuPopAll();
            uiMenuDialogMesgTimed(
                LANG_ZCOM_STRING_RES_KeyPad_Lock_Prompt_Title,
                langGet(LANG_ZCOM_STRING_RES_KeyPad_Lock_Msg));
        }
    }
}

static void uiClearButtonTask(struct pollerTask *taskP)
{
    //
    // Timeout, clear one line
    //
    int i = 0;
    UIMenu* menu = (UIMenu*)pollerTaskCookieGet(taskP);

    if (menu->m.text.str[0] &&
        menu->m.text.cursorPos > 0 &&
        (menu->type == UI_MENU_TYPE_NUM_ENTRY ||
        menu->type == UI_MENU_TYPE_HEX_ENTRY ||
        menu->type == UI_MENU_TYPE_TEXT_ENTRY ||
        menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY || //2007/04/09  lizhen
		menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY ||
		menu->type == UI_MENU_TYPE_PKEY_TEXT_ENTRY))	//Added by Mike
    {
        int remove =  1 + Pw_FontStringFit(
                menu->m.text.font,
                menu->m.text.str + ((menu->m.text.lineIdx[0] < 0)?0:menu->m.text.lineIdx[0]),
                menu->m.text.cursorX
                );

        for (i = menu->m.text.cursorPos;menu->m.text.str[i] ;i++)
        {
            menu->m.text.str[i - remove] = menu->m.text.str[i];
        }
        menu->m.text.str[i - remove] = '\0';
        menu->m.text.cursorPos -= remove;
        menu->m.text.lastKeySym = KEYSYM_UNDEF;

        if ( !menu->m.text.str[0] )
        {
            uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
			if(menu->type == UI_MENU_TYPE_PKEY_TEXT_ENTRY) //by Mike
			{
				uiMenuSoftKeyPositive(menu->softKey, menu->m.text.pkeyTextNilEntryIdx);
			}
        }
        else
        {
            uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Clear);
			if(menu->type == UI_MENU_TYPE_PKEY_TEXT_ENTRY) //by Mike
			{
				uiMenuSoftKeyPositive(menu->softKey, menu->m.text.pkeyTextNotNilEntryIdx);
			}
        }

        Pw_WindowRepaint(&uiMenuS.MenuWin);
    }

    if (menu->m.text.str[0])
    {
        //
        // String is not empty still, reschedule
        //
        pollerTaskCookieSet(&ClearButtonTask, (void*)menu);
        pollerTaskTimeoutRel(&ClearButtonTask,
            UI_CLEAR_KEY_PRESS_SECONDS * pollerClockHzGet());
    }
}


// junky 2007-4-24
static void zcom_uiRepaintForScrollStrTask(struct pollerTask *taskP)
{
    //UIMenu      *menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];
    //int*        pStrPos = NULL;

    //switch(menu->type)
    //{
    //case UI_MENU_TYPE_LIST:
    //    {
    //        pStrPos = &menu->m.list.nPosList;
    //        break;
    //    }
    //case UI_MENU_TYPE_USER:
    //    {
    //        pStrPos = &menu->m.user.nPosUser;
    //        break;
    //    }
    //case UI_MENU_TYPE_TEXT_EX_ENTRY:
    //    {
    //        //pStrPos = &menu->m.text.nPosText;
    //        break;
    //    }

    //default:
    //    break;
    //}

    //if (pStrPos != NULL)
    //{
    //    (*pStrPos)++;
    //}

    Pw_WindowRepaint(&uiMenuS.MenuWin);
}

/* Forward declarations */
static void uiMenuRefreshTask(struct pollerTask *taskP);

/*
 * uiMenuInit -- one time initialization
 */
void uiMenuInit(Pw_Display* dpy)
{
    uiMenuS.MenuTop = -1;

    extern void zcom_uiCallBack(enumState State, enumErrorCode ErrorCode);
    zcom_StateDispatchRegisterCallBack(&zcom_uiCallBack);

    // TODO? : set ShowShortcutHints from user configuration.
    uiMenuS.ShowShortcutHints = CFG_MENU_SHOW_SHORT_CUT_HINTS;

    Pw_Window* rootWin = Pw_DisplayGetRootWindow(dpy);
    Pw_Coord w, h;

    Pw_WindowGetSize(rootWin, &w, &h);
    Pw_WindowCreate(rootWin, 0, 0, w, h, &uiMenuS.MenuWin);

    mainthreadTaskInit(&uiMenuS.MenuRefreshTask,
            "MenuRefreshTask", uiMenuRefreshTask);
    pollerTaskPrioritySet(&uiMenuS.MenuRefreshTask,
        CONFIG_PRIORITY_DISPLAY_REFRESH );

    mainthreadTaskInit(&ClearButtonTask, "clearButtonTask",
                   uiClearButtonTask);
    pollerTaskPrioritySet(&ClearButtonTask, CONFIG_PRIORITY_UI);

    mainthreadTaskInit(&zcom_HoldStarKeyTask, "HoldStarKeyTask",
                   zcom_uiHoldStarKeyTask);
    pollerTaskPrioritySet(&zcom_HoldStarKeyTask, CONFIG_PRIORITY_UI);

    if (zcom_uiModelCapabilityIsEnabled(eModelCapability_PoundKeyEnterP2P))
    {
        mainthreadTaskInit(&zcom_HoldPoundKeyTask, "HoldPoundKeyTask",
            zcom_uiHoldPoundKeyTask);
        pollerTaskPrioritySet(&zcom_HoldPoundKeyTask, CONFIG_PRIORITY_UI);
    }

    mainthreadTaskInit(&zcom_uiKeyPadLockS.zcom_AutoKeyPadLockTask,
                        "KeyPadLockTask",
                        zcom_uiAutoKeyPadLockTask);
    pollerTaskPrioritySet(&zcom_uiKeyPadLockS.zcom_AutoKeyPadLockTask,
                        CONFIG_PRIORITY_UI);
    mainthreadTaskInit(&zcom_uiKeyPadLockS.zcom_KeyPadWaitNextKeyTask,
                        "KeyPadLockTask",
                        zcom_uiKeyPadWaitNextKeyTask);
    pollerTaskPrioritySet(&zcom_uiKeyPadLockS.zcom_KeyPadWaitNextKeyTask,
                        CONFIG_PRIORITY_UI);

    // junky 2007-4-24
    // add a tast for scrolling string
    mainthreadTaskInit(&uiMenuS.zcom_RepaintScrollStrTask,
        "RepaintForScrollStrTask",
        zcom_uiRepaintForScrollStrTask);
    pollerTaskPrioritySet(&uiMenuS.zcom_RepaintScrollStrTask,
        CONFIG_PRIORITY_DISPLAY_REFRESH);
    zcom_InitScrollingStrStatusForUser();

    zcom_uiKeyPadLockS.zcom_KeyPadLockState = ZCOM_KEYPAD_UNLOCK;
    zcom_uiKeyPadLockS.zcom_WaitPowerKeyReleased = false;
    zcom_StartAutoKeypadLockJob();

    return;
}


/*
 * Are shortcut hints being shown?
 */
int uiMenuShortcutHintShown(void)
{
    return uiMenuS.ShowShortcutHints;
}

/*
 * poller callback to update current menu (see uiMenuRefresh)
 */
static void uiMenuRefreshTask(struct pollerTask *taskP)
{   /* poller callback to update current menu */
    Pw_WindowRepaint(&uiMenuS.MenuWin);
}

/*
 * uiMenuLevelGet -- return current menu level
 */
int uiMenuLevelGet(void)
{
    return uiMenuS.MenuTop;
}

/*
 * uiMenuTopGet -- return top menu
 */
UIMenu* uiMenuTopGet(void)
{
    return &uiMenuS.MenuStack[uiMenuS.MenuTop];
}

void zcom_uiMenuListReInit_ForLevel(const UIMenuListInit *init, int level)
{
    debugf("//////////zcom_uiMenuListReInit_ForLevel\\\\\\\\\\");

    UIMenu *menu = &uiMenuS.MenuStack[level];

    menu->type                = UI_MENU_TYPE_LIST;
    menu->title               = init->title;
    menu->cleanupCB           = init->cleanupCB;
    menu->cleanupCBArg        = init->cleanupCBArg;
    menu->m.list.nNodes       = init->nNodes;
    menu->currNode            = init->currNode;
    menu->m.list.dispTopNode  = 0;
    menu->m.list.nDispLines   = init->nDispLines;
    menu->m.list.nodes        = init->nodes;
    menu->m.list.valueList    = init->valueList;
    menu->m.list.configList   = init->configList;
    menu->m.list.configNode   = init->configNode;
    menu->m.list.readonly     = init->readonly;
    menu->m.list.autoAdvance  = init->autoAdvance;
    menu->m.list.nodeCntCB    = init->nodeCntCB;
    menu->m.list.nodeTextCB   = init->nodeTextCB;
    menu->m.list.nodeSelectCB = init->nodeSelectCB;
    menu->m.list.nodeTalkCB   = init->nodeTalkCB;
    menu->m.list.nodeCBArg    = init->nodeCBArg;
    if ( menu->m.list.nodeCntCB ) {
        menu->m.list.nodeCntCB(&menu->currNode,
                               &menu->m.list.nNodes,
                               menu->m.list.nodeCBArg);
    }
    if ( menu->currNode >= menu->m.list.nNodes ) {
        menu->currNode = menu->m.list.nNodes - 1;
    }

    uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Select);
    uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
}

/*
 * uiMenuRefresh -- schedule redraw of currently displayed menu
 */
void uiMenuRefresh(void)
{
    pollerTaskReady(&uiMenuS.MenuRefreshTask); //does menu repaint
}

/*
 * uiMenuWindowRepaint -- redraw currently displayed menu NOW
 *      regardless of other priorities.
 *      Use with great discretion!
 */
void uiMenuWindowRepaint(void)
{
    Pw_WindowRepaint(&uiMenuS.MenuWin);
    dispFlush();        /* force immediate update */
}


/*
 * uiMenuPush -- descend into menu hierarchy.
 *      Returns UIMenu* which must be filled out by caller.
 */
UIMenu *uiMenuPush(int newMenuType)
{
    assert(uiMenuS.MenuTop < UI_MENU_MAXDEPTH - 1);
    UIMenu *menu;

    /*
     * If the top-level menu is a timeout dialog, then pop the
     * dialog... unless it is specifically protected from that.
     * HACK: also pop any sliders......
     * this needs more thought.
     */
    while ( uiMenuS.MenuTop >= 0 ) {
        menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

        /* everything but scale trumps a dialog */
        if ( menu->type == UI_MENU_TYPE_DIALOG &&
                menu->m.dialog.protect == 0 &&
                newMenuType != UI_MENU_TYPE_SCALE ) {
            uiMenuPop(0);
            continue;
        }

        /* everything trumps a scale */
        if ( menu->type == UI_MENU_TYPE_SCALE ) {
            uiMenuPop(0);
            continue;
        }

        /*
         * -----------------------------------------------------------
         * To be safe, stop any timeouts...
         * TODO: although an ongoing timeout probably indicates the
         * need for deeper analysis of the state machine.... ?
         * -----------------------------------------------------------
         */
        switch ( menu->type ) {
            case UI_MENU_TYPE_NUM_ENTRY:
                if (pollerTaskIsQueued(&menu->m.text.zeroKeyTimeoutTask))
                {
                    debugf("BOTCH: improper handling 0 Key?\n");
                    pollerTaskStop(&menu->m.text.zeroKeyTimeoutTask);
                    menu->m.text.lastKeySym = KEYSYM_UNDEF;     // %%% ?
                }
            case UI_MENU_TYPE_HEX_ENTRY:
            case UI_MENU_TYPE_TEXT_ENTRY:
            case UI_MENU_TYPE_TEXT_EX_ENTRY:    //2007/04/09    lizhen
			case UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY: //by Mike
			case UI_MENU_TYPE_PKEY_TEXT_ENTRY: //by Mike
                if (pollerTaskIsQueued(&menu->m.text.keyTimeoutTask))
                {
                    debugf("BOTCH: improper handling?");
                    pollerTaskStop(&menu->m.text.keyTimeoutTask);
                    menu->m.text.lastKeySym = KEYSYM_UNDEF;     // %%% ?
                }
                break;

            case UI_MENU_TYPE_SCALE:
                if (pollerTaskIsQueued(&menu->m.scale.timeoutTask))
                {
                    debugf("BOTCH: improper handling?");
                    pollerTaskStop(&menu->m.scale.timeoutTask);
                }
                break;

            case UI_MENU_TYPE_DIALOG:
                if (pollerTaskIsQueued(&menu->m.dialog.timeoutTask))
                {
                    debugf("BOTCH: improper handling?");
                    pollerTaskStop(&menu->m.dialog.timeoutTask);
                }
                break;

            case UI_MENU_TYPE_STATUS:
                if (pollerTaskIsQueued(&menu->m.status.timeoutTask))
                {
                    pollerTaskStop(&menu->m.status.timeoutTask);
                }
                break;

            default:
                break;
        }
        break;
    }

    /*
     * Push the stack
     */
    uiMenuS.MenuTop++;
    menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

    memset(menu, 0, sizeof(uiMenuS.MenuStack[0]));

    return menu;
}


/*
 * Activate and display the top-level (current) menu.
 */
void uiMenuActivate(void)
{
    UIMenu *menu;

    if ( uiMenuS.MenuTop < 0 ) {
        /*
         * Menu stack is empty - remove menu window
         */
        uiMenuS.MenuWinMapped = 0;
        Pw_WindowUnmap(&uiMenuS.MenuWin);
        return;
    }
    menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

    /*
     * Initialize any poller tasks as needed
     */
    switch ( menu->type ) {
        case UI_MENU_TYPE_LIST:
            break;

        case UI_MENU_TYPE_NUM_ENTRY:
        case UI_MENU_TYPE_HEX_ENTRY:
        case UI_MENU_TYPE_TEXT_ENTRY:
		case UI_MENU_TYPE_PKEY_TEXT_ENTRY: //by Mike
            // Initialization moved to where we create the menu.
            // Just to be safe, i left the stop here.
            // This probably isn't needed.   -Ted
            pollerTaskStop(&menu->m.text.keyTimeoutTask);
            break;

        case UI_MENU_TYPE_TEXT_EX_ENTRY://2007/04/09    lizhen
		case UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY: //by Mike
            pollerTaskStop(&menu->m.text.keyTimeoutTask);
         break;

        default:
            break;
    }

    if ( !uiMenuS.MenuWinMapped ) {
        /*
         * First menu - setup and map menu window
         */
        Pw_WindowSetRepaintCallback(&uiMenuS.MenuWin, uiMenuWinRepaint);

        //Pw_WindowSetEventCallback(&uiMenuS.MenuWin, uiMenuWinEvents);

        Pw_WindowMap(&uiMenuS.MenuWin);

        Pw_WindowSetFocusAccept(&uiMenuS.MenuWin, TRUE);
        Pw_WindowSetFocus(&uiMenuS.MenuWin);
        Pw_WindowPlaceOnTop(&uiMenuS.MenuWin);
        uiMenuS.MenuWinMapped = 1;
    } else {
        Pw_WindowRepaint(&uiMenuS.MenuWin);
    }
    /*
     * Cancel any key repeat: repeat shouldn't continue across
     * menu boundaries.
     */
    uiKeyRepeatCancel();

}



/*
 * Clean up the given menu in preparation for popping OR reuse in place.
 */
/*private*/ void uiMenuClean( UIMenu *menu )
{
    /*
     * Clean up any poller tasks as needed
     */
    switch ( menu->type ) {
        case UI_MENU_TYPE_LIST:
            break;

        case UI_MENU_TYPE_NUM_ENTRY:
            pollerTaskDestroy(&menu->m.text.zeroKeyTimeoutTask);
        case UI_MENU_TYPE_HEX_ENTRY:
        case UI_MENU_TYPE_TEXT_ENTRY:
        case UI_MENU_TYPE_TEXT_EX_ENTRY://2007/04/09    lizhen
		case UI_MENU_TYPE_PKEY_TEXT_ENTRY: //by Mike
		case UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY:
            pollerTaskDestroy(&menu->m.text.keyTimeoutTask);
            break;

        case UI_MENU_TYPE_SCALE:
            pollerTaskDestroy(&menu->m.scale.timeoutTask);
            break;

        case UI_MENU_TYPE_DIALOG:
            pollerTaskDestroy(&menu->m.dialog.timeoutTask);
            break;

        case UI_MENU_TYPE_STATUS:
            pollerTaskDestroy(&menu->m.status.timeoutTask);
            break;

        default:
            break;
    }
    /*
     * Any user supplied cleanups
     * These MUST NOT mess with the menu stack.
     */
    void (*cleanupCB)(void *) = menu->cleanupCB;
    void *cleanupCBArg = menu->cleanupCBArg;
    memset(menu, 0, sizeof(*menu));     // set menu state to inactive
    if ( cleanupCB ) (*cleanupCB)( cleanupCBArg );
}

/*
 * Pop the top-level menu, and if activate is true, then
 * activate the new top-level menu.
 */
void uiMenuPop(int activate)
{
    UIMenu *menu;

    /*
    * Temporary hack for now: don't pop the top menu or worse.
    */
    if ( uiMenuS.MenuTop <= 0 ) return;

    menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];
    uiMenuClean(menu);

    /*
     * Pop the stack and activate the new top-level menu
     */
    uiMenuS.MenuTop--;

    menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

    if ( menu->type == UI_MENU_TYPE_INACTIVE || menu->popWithParent ) {
        uiMenuPop(activate);
    } else if ( activate ) {
        /* Auto-advance feature scrolls down in a list when
         *      we return to it...
         *      however, if at a "config" choice then there is no
         *      point in scrolling down to other choices, so instead
         *      we should pop out further.
         */
        if ( menu->type == UI_MENU_TYPE_LIST &&
                menu->m.list.autoAdvance ) {
            if ( menu->m.list.configList ) {
                uiMenuPop(activate);
                return;
            } else if ( menu->currNode < menu->m.list.nNodes-1 ) {
                menu->currNode++;
            } else {
                uiMenuPop(activate);
                return;
            }
        }
        uiMenuActivate();
    }
}

/*
 * Pop all the menu entries down to but not including the given level,
 * and activate the menu if requested.
 */
void uiMenuPopLevel(int level, int activate)
{
    if ( level >= 0 && level <= uiMenuS.MenuTop ) {
        while ( uiMenuS.MenuTop > level ) {
            uiMenuPop(0);
        }
        if ( activate ) {
            uiMenuActivate();
        }
    } else {
        debugf("BOTCH: uiMenuPopLevel(%d,%d)", level, activate);
    }
}

/*
 * Pop just the given menu level.  If this is the top-level
 * menu then this just calls uiMenuPop().  Otherwise, flag
 * the given menu so it is automatically popped with the
 * parent later.
 */
void uiMenuPopLevelOnly(UIMenu * level, int activate)
{
    assert(level != NULL);
    assert(level != &uiMenuS.MenuStack[0]);
    assert(level <= &uiMenuS.MenuStack[uiMenuS.MenuTop]);
    if ( level == &uiMenuS.MenuStack[uiMenuS.MenuTop] ) {
        uiMenuPop(activate);
    } else {
        level->popWithParent = 1;
    }
}

int uiMenuLevelTop(void)
{
    return uiMenuS.MenuTop;
}

/*
 * Pop all the menu entries except the very last one,
 * which is then activated.
 */
void uiMenuPopAll(void)
{
    uiMenuPopLevel(0 /*level*/, 1 /*activate*/);
}



void uiMenuListInit(const UIMenuListInit *init)
{
    UIMenu *menu = uiMenuPush(UI_MENU_TYPE_LIST);

    menu->type               = UI_MENU_TYPE_LIST;
    menu->title              = init->title;
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;
    menu->m.list.nNodes      = init->nNodes;
    menu->currNode    = init->currNode;
    menu->m.list.dispTopNode = 0;
    menu->m.list.nDispLines  = init->nDispLines;
    menu->m.list.nodes       = init->nodes;
    menu->m.list.valueList   = init->valueList;
    menu->m.list.configList  = init->configList;
    menu->m.list.configNode  = init->configNode;
    menu->m.list.readonly    = init->readonly;
    menu->m.list.mustSelect  = init->mustSelect;
    menu->m.list.autoAdvance = init->autoAdvance;
    menu->m.list.nodeCntCB    = init->nodeCntCB;
    menu->m.list.nodeTextCB   = init->nodeTextCB;
    menu->m.list.nodeSelectCB = init->nodeSelectCB;
    menu->m.list.nodeTalkCB   = init->nodeTalkCB;
    menu->m.list.nodeCBArg    = init->nodeCBArg;

    menu->m.list.nPosList    = 0;

    menu->m.list.nScrollWaitCount = 0;

    // Tianjun 2007-3-27 13:43
    menu->m.list.nodeSelectChangedCB = init->nodeSelectChangedCB;

    if ( menu->m.list.nodeCntCB ) {
        menu->m.list.nodeCntCB(&menu->currNode,
                               &menu->m.list.nNodes,
                               menu->m.list.nodeCBArg);
    }
    if ( menu->currNode >= menu->m.list.nNodes ) {
        menu->currNode = menu->m.list.nNodes - 1;
    }

    if ( menu->m.list.readonly )
    {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
    }
    else if (menu->m.list.mustSelect)
    {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Done);
    }
    else
    {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
        uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Select);
    }
    uiMenuActivate();
}


/*===========================================================================*/
/*==============  Some Menu Sub-Classes =====================================*/
/*===========================================================================*/

/*
 * Figure out at how many bytes in should we break the string
 * in order to fit in the display.
 */
static int uiTextBreakPoint(
        Pw_GC *gc,
        Pw_Coord w,             /* pixels wide to display in */
        const char *str         /* string to display */
        )
{
    /*
     * Figure out how many bytes of input chars will fit on this line.
     * Note unicode! TODO: make sure this is truly unicode compatible.
     * We can if need be chop it at that size, and continue on next line,
     * but it looks messy... if possible, we go back to preceding space char.
     */
    int nBytes = Pw_GCStringFit(gc, str, w );
    if ( str[nBytes] ) {
        /*
         * Not the whole string - next try to break just after
         * the last space or some punctuation.
         */
        int k;
        for ( k = nBytes - 1 ; k > 0 ; k-- ) {
            if ( str[k] == ' ' || str[k] == ':' || str[k] == '/') {
                nBytes = k + 1;      /* line break just after space */
                /* the space will be trimmed when displayed; no problem */
                break;
            }
        }
    }
    while ( str[nBytes] == ' ' ) nBytes++;      /* catch any more spaces */
    if ( nBytes <= 0 ) {
        debugf("uiMenuDialogTextLayout: BOTCH nBytes = %d\n", nBytes);
    }
    return nBytes;
}




void uiMenuTextGet(const UIMenuText *text, char *buf, int bufsize)
{
    if ( text->textCB ) {
        (*text->textCB)(buf, bufsize, text->textIdx, 0, text->textCBarg);
    } else {
        sprcpy(buf, bufsize, langGet(text->textIdx));
    }
}

static int uiMenuTextEntryLayout(
        UIMenu *menu,
        Pw_GC *gc,
        int font,
        int mustFit,
        Pw_Coord y0,
        Pw_Coord y1,
        Pw_Coord x,
        Pw_Coord w
    )
{
    int nLines, i, lineCnt = 0, cursorLine = 0;
    int fontHeight;
    const char *str = menu->m.text.str;

    Pw_GCSetFont(gc, dispFontGet(font));
    nLines = (y1 - y0 - 2) / Pw_GCGetFontHeight(gc);

    /*
     * Figure out the layout by breaking each line as necessary.
     */
    for ( i = 0 ; str[i] ; )
    {
        /*
         * Figure out how many characters will fit on this line
         */
        int nBytes = Pw_GCStringFit(gc, str+i, w - 2);
        if ( i < menu->m.text.cursorPos )
        {
            /*
             * Cursor is on this line or later
             */
            cursorLine = lineCnt;
        }
        if ( nBytes <= 0 )
        {
            debugf("uiMenuTextEntryLayout: BOTCH nBytes = %d\n", nBytes);
            break;
        }
        i += nBytes;
        lineCnt++;
    }

    /*
     * Make sure the visible portion of the text entry area
     * includes the line that the cursor is on
     */
    if ( menu->m.text.dispTopLine > cursorLine ) {
        menu->m.text.dispTopLine = cursorLine;
    }
    if ( menu->m.text.dispTopLine + nLines <= cursorLine ) {
        menu->m.text.dispTopLine = cursorLine - nLines + 1;
    }

    //2007/02/07    lizhen
    //draw a scroll bar
    if(nLines >= lineCnt)
    {
        menu->m.text.dispTopLine = 0;
    }
    else
    {
        if (menu->type != UI_MENU_TYPE_TEXT_EX_ENTRY
			&& menu->type != UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY) //by Mike
        {
            uiMenuScrollBarDraw(
                gc,
                w+1,
                y0+1,
                y1,
                menu->m.text.dispTopLine,
                lineCnt -nLines + 1);
        }
    }

    /*
     * Remember the font for cursor motion calcs
     */
    menu->m.text.font = Pw_GCGetFont(gc);

    /*
     * Now render the text edit area
     */
    Pw_GCSetColor(gc, pw_black_pixel);
    Pw_DrawRect(gc, x, y0, w, y1 - y0);
    y0++;
    lineCnt = 0;
    fontHeight = Pw_GCGetFontHeight(gc);
    for ( i = 0 ; i < 4 ; i++ ) {
        menu->m.text.lineIdx[i] = -1;
    }
    if ( !str[0] ) {
        /*
         * Render just a vertical cursor...
         */
        int cursorX = 2;
        Pw_DrawLine(gc, cursorX, y0, cursorX, y0 + fontHeight - 1);
        menu->m.text.cursorX = cursorX - 2;
        return 0;
    }
    for ( i = 0 ; str[i] ; ) {
        /*
         * Figure out how many characters will fit on this line
         */
        if ( cursorLine - 1 <= lineCnt && lineCnt <= cursorLine + 2 ) {
            /*
             * Remember the byte index of the character at the start of
             * the line -1, 0, +1, +2 relative to the cursor line.
             * This is so the up and down keys can figure out
             * where to move the cursor.
             */
            menu->m.text.lineIdx[lineCnt - cursorLine + 1] = i;
        }
        int nBytes = Pw_GCStringFit(gc, str+i, w - 2);
        if ( nBytes <= 0 ) break;

        if ( lineCnt >= menu->m.text.dispTopLine
                && lineCnt < menu->m.text.dispTopLine + nLines ) {
            /*
             * Display this line
             */
            Pw_DrawStringNAnchor(gc, x + 2, y0,
                str+i, nBytes, Pw_TextAnchor_TopLeft);

            if ( cursorLine == lineCnt ) {

                if ( menu->m.text.lastKeySym == KEYSYM_UNDEF ) {
                    int cursorX;
                    /*
                     * We are not entering a character currently.
                     * Render a vertical cursor between characters
                     */
                    cursorX = 2 + Pw_GCGetStringNWidth(gc,
                        str+i, menu->m.text.cursorPos-i);
                    Pw_DrawLine(gc, cursorX, y0, cursorX, y0 + fontHeight - 1);
                    menu->m.text.cursorX = cursorX - 2;
                } else if ( menu->m.text.cursorPos >= 1 ) {
                    int cursorX, charX;
                    /*
                     * We are entering a character.
                     * Render an underline cursor for the changing character,
                     * which is immediately prior to the cursor.
                     */
                    int dy = Pw_GCGetFontAscent(gc) + Pw_GCGetFontDescent(gc);
                    const char *prevCh;

                    cursorX = 2 + Pw_GCGetStringNWidth(gc,
                        str+i, menu->m.text.cursorPos-i );
                    prevCh = unicodeUtf8PrevChar(
                                    &str[menu->m.text.cursorPos],
                                    &str[0]);
                    charX = Pw_GCGetStringNWidth(gc, prevCh, 1);
                    Pw_DrawLine(gc, cursorX - charX, y0 + dy,
                                    cursorX - 1, y0 + dy);
                    menu->m.text.cursorX = cursorX - 2;
                }
            }
            y0 += fontHeight;
        }
        if ( nBytes <= 0 ) {
            debugf("uiMenuTextEntryLayout: BOTCH nBytes = %d\n", nBytes);
        }
        i += nBytes;
        lineCnt++;
    }

    return 0;
}

//static void uiMenuTextEntryLayoutBiggestFont(
//        UIMenu *menu,
//        Pw_GC *gc,
//        Pw_Coord y0,
//        Pw_Coord y1,
//        Pw_Coord x,
//        Pw_Coord w
//    )
//{
//    /*
//     * Try to layout with the larger font.  If that doesn't work
//     * then switch to the regular or small font.
//     */
//    uiMenuTextEntryLayout(
//        menu, gc, DISP_FONT_TYPE_LARGE, 1/*mustfit*/,
//            y0, y1, 0, w - 1)  &&
//    uiMenuTextEntryLayout(
//        menu, gc, DISP_FONT_TYPE_REGULAR, 1/*mustfit*/,
//            y0, y1, 0, w - 1)  &&
//    uiMenuTextEntryLayout(
//        menu, gc, DISP_FONT_TYPE_SMALL, 0/*mustfit*/,
//            y0, y1, 0, w - 1);
//}

int uiMenuDialogTextLayout(
        const char *str,
        Pw_GC *gc,
        int font,
        int mustFit,
        Pw_Coord y0,
        Pw_Coord y1,
        Pw_Coord x,
        Pw_Coord w,
        int drawBox
    )
{
    int nLines, i, lineCnt = 0;
    int fontHeight;

    Pw_GCSetFont(gc, dispFontGet(font));
    nLines = (y1 - y0 - (drawBox ? 2 : 0)) / Pw_GCGetFontHeight(gc);

    /*
     * Figure out the layout by breaking each line as necessary.
     */
    for ( i = 0 ; str[i] ; ) {
        /*
         * Figure out how many characters will fit on this line
         */
        int nBytes = uiTextBreakPoint(gc, w-2/*fudge*/, str+i);
        if ( nBytes <= 0 ) {
            debugf("uiMenuDialogTextLayout: BOTCH nBytes = %d\n", nBytes);
            return 1;   /* error */
        }
        i += nBytes;
        lineCnt++;
    }

    if ( mustFit && lineCnt > nLines ) {
        /*
        * Doesn't fit - return error
        */
        return 1;
    }

    /*
     * Now render the text edit area
     */
    Pw_GCSetColor(gc, pw_black_pixel);
    if ( drawBox ) {
        Pw_DrawRect(gc, x, y0, w, y1 - y0);
        y0++;
    }
    fontHeight = Pw_GCGetFontHeight(gc);
    for ( lineCnt = 0, i = 0 ; str[i] && lineCnt < nLines; ) {
        /*
         * Figure out how many characters will fit on this line
         */
        int nBytes = uiTextBreakPoint(gc, w-2/*fudge*/, str+i);
        if ( nBytes <= 0 ) {
            debugf("uiMenuDialogTextLayout: BOTCH nBytes = %d\n", nBytes);
            break;
        }
        Pw_DrawStringNAnchor(gc, x + 2, y0,
                str+i, nBytes, Pw_TextAnchor_TopLeft);
        y0 += fontHeight;
        i += nBytes;
        lineCnt++;
    }

    return 0;
}

void uiMenuDialogTextLayoutBiggestFont(
        const char *str,
        Pw_GC *gc,
        Pw_Coord y0,
        Pw_Coord y1,
        Pw_Coord x,
        Pw_Coord w,
        int drawBox
    )
{
    /*
     * Try to layout with the larger font.  If that doesn't work
     * then switch to the regular or small font.
     */
    uiMenuDialogTextLayout(
        str, gc, DISP_FONT_TYPE_LARGE, 1/*mustfit*/,
            y0, y1, 0, w - 1, 0) &&
    uiMenuDialogTextLayout(
        str, gc, DISP_FONT_TYPE_REGULAR, 1/*mustfit*/,
            y0, y1, 0, w - 1, 0) &&
    uiMenuDialogTextLayout(
        str, gc, DISP_FONT_TYPE_SMALL, 0/*mustfit*/,
            y0, y1, 0, w - 1, 0);
}

/*
 * Render a vertical scroll bar from (w,y0) to (w,y1)
 * with the given value and maximum value.
 */
void uiMenuScrollBarDraw(
    Pw_GC *gc,
    Pw_Coord w,
    Pw_Coord y0,
    Pw_Coord y1,
    unsigned value,     /* in range 0 to max-1 */
    unsigned max
    )
{
    int tabY, tabH, tabY0, tabH0;

    if ( max < 1 ) max = 1;
    if ( value >= max ) value = max - 1;
    Pw_GCSetColor(gc, pw_white_pixel);
    Pw_FillRect(gc, w - CFG_SCROLL_BAR_WIDTH, y0, w - 1, y1 - 1 - y0);
    Pw_GCSetColor(gc, pw_black_pixel);
    Pw_DrawLine(gc, w - (CFG_SCROLL_BAR_TABWIDTH + 1) / 2, y0,
                    w - (CFG_SCROLL_BAR_TABWIDTH + 1) / 2, y1 - 2);
    tabY = y0 + (y1 - y0) * value / max;
    tabH = (y1 - y0) / max;
    if ( tabH < 2 ) tabH = 2;
    tabH0 = tabH + 2;
    tabY0 = tabY - 1;
    if ( tabY0 < y0 ) {
        tabY0 = y0;
        tabH0--;
    }
    if ( tabY0 + tabH0 > y1 ) {
        tabH0 = y1 - tabY0;
    }
    /*
     * Draw a white rectangle two pixels higher (one above and
     * one below), with a black rectangle on top
     */
    Pw_GCSetColor(gc, pw_white_pixel);
    Pw_FillRect(gc, w - CFG_SCROLL_BAR_TABWIDTH, tabY0,
                    CFG_SCROLL_BAR_TABWIDTH, tabH0);
    Pw_GCSetColor(gc, pw_black_pixel);
    Pw_FillRect(gc, w - CFG_SCROLL_BAR_TABWIDTH, tabY,
                    CFG_SCROLL_BAR_TABWIDTH, tabH);
}


//junky 2007-4-20
void zcom_DrawScrollingStringForList(
                                     Pw_GC*   gc,
                                     Pw_Coord x,
                                     Pw_Coord y,
                                     const Pw_Char* string,
                                     Pw_TextAnchorType anchor,
                                     int width)
{
    char strTem[UI_MENU_MAXSTR] = {};
    UIMenu *menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];
    int       ii = 0;
    int       jj = menu->m.list.nPosList;

    if (menu->m.list.nScrollWaitCount != 0 && menu->m.list.nScrollWaitCount != 1)
    {
        while ((strTem[ii] = string[jj]) != '\0')
        {
            ii ++;
            jj ++;
        }

        Pw_DrawStringAnchor(gc, x, y, strTem, anchor);

        if (Pw_GCGetStringWidth(gc, strTem) < width)
        {
            menu->m.list.nPosList = 0;

            menu->m.list.nScrollWaitCount = 0;

            pollerTaskTimeoutRel(&uiMenuS.zcom_RepaintScrollStrTask, pollerClockMsecToTicks(ZCOM_REPAINT_INTERVAL));

            return;
        }

        int nBytes = 0;
        Pw_FontGetUtf8Char(strTem, &nBytes);
        menu->m.list.nPosList += nBytes;
    }
    else
    {
        Pw_DrawStringAnchor(gc, x, y, string, anchor);
        menu->m.list.nScrollWaitCount++;
    }

    //start the task
    pollerTaskTimeoutRel(&uiMenuS.zcom_RepaintScrollStrTask, pollerClockMsecToTicks(ZCOM_REPAINT_INTERVAL));
}

//junky 2007-4-20
void zcom_DrawScrollingStringForText(
                                     Pw_GC*   gc,
                                     Pw_Coord x,
                                     Pw_Coord y,
                                     const Pw_Char* string,
                                     Pw_TextAnchorType anchor,
                                     int width)
{
    char strTem[UI_MENU_MAXSTR] = {};
    UIMenu *menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];
    int       ii = 0;
    int       jj = menu->m.text.nPosText;

    if (menu->m.text.nScrollWaitCount != 0 && menu->m.text.nScrollWaitCount != 1)
    {
        while ((strTem[ii] = string[jj]) != '\0')
        {
            ii ++;
            jj ++;
        }

        Pw_DrawStringAnchor(gc, x, y, strTem, anchor);

        if (Pw_GCGetStringWidth(gc, strTem) < width)
        {
            menu->m.text.nPosText = 0;

            menu->m.text.nScrollWaitCount = 0;

            pollerTaskTimeoutRel(&uiMenuS.zcom_RepaintScrollStrTask, pollerClockMsecToTicks(ZCOM_REPAINT_INTERVAL));

            return;
        }

        int nBytes = 0;
        Pw_FontGetUtf8Char(strTem, &nBytes);
        menu->m.text.nPosText += nBytes;
    }
    else
    {
        Pw_DrawStringAnchor(gc, x, y, string, anchor);
        menu->m.text.nScrollWaitCount++;
    }

    //start the task
    pollerTaskTimeoutRel(&uiMenuS.zcom_RepaintScrollStrTask, pollerClockMsecToTicks(ZCOM_REPAINT_INTERVAL));
}

void zcom_InitScrollingStrStatusForUser()
{
    UIMenu *menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

    menu->m.user.nPosUser = 0;
    RepaintScrollStrStep = ZCOM_REPAINT_STEP1;
    strGlobeSource[0] = '\0' ;
}

void zcom_DrawScrollingStringForUser(
                                     Pw_GC*   gc,
                                     Pw_Coord x,
                                     Pw_Coord y,
                                     const Pw_Char* string,
                                     Pw_TextAnchorType anchor,
                                     int canfit)
{
    char strTemSource[UI_MENU_MAXSTR + 10] = {0};
    char strTem[UI_MENU_MAXSTR] = {0};
    UIMenu *menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];
    int      len = strlen(string);
    int       ii = menu->m.user.nPosUser;
    int       jj = 0;

    strcpy(strTemSource, string);
    if (len + 8 <  UI_MENU_MAXSTR)
    {
        for (int m = len; m < len + 7; m ++)
        {
            strTemSource[m] = ' ';
        }
        strTemSource[len + 7] = 0;
    }

    //debugf("enter ii=%d ZCOM_REPAINT_STEP2-22-->menu->nPos=%d strTemSource=%s\n",ii, menu->m.user.nPosUser, strTemSource);


    //if (LastnPos == menu->m.user.nPosUser)
    //{
    //    Pw_DrawStringAnchor(gc, x, y, strGlobeSource, anchor);

    //    int nBytes = 0;
    //    Pw_FontGetUtf8Char(strGlobeSource, &nBytes);
    //    menu->m.user.nPosUser += nBytes;

    //    pollerTaskTimeoutRel(&uiMenuS.zcom_RepaintScrollStrTask, pollerClockMsecToTicks(10));
    //    return;
    //}

    int temLen = strlen(strTemSource);

    switch(RepaintScrollStrStep)
    {
    case ZCOM_REPAINT_STEP1:
        {
            if (ii + canfit >= temLen)
            {
                RepaintScrollStrStep = ZCOM_REPAINT_STEP2;
                menu->m.user.nPosUser = -1;
                jj = temLen - canfit;
                for (int k = 0; k < canfit; k++)
                {
                    strTem[k] = strTemSource[jj];
                    jj ++;
                }
                strTem[canfit] = '\0';
                strcpy(strGlobeSource, strTem);
            }
            else
            {
                for (int j = 0; j < canfit; j++)
                {
                    strTem[j] = strTemSource[ii];
                    ii ++;
                }
                strTem[canfit] = '\0';
            }
        }
        break;
    case ZCOM_REPAINT_STEP2:
        {
            //debugf(" enter ZCOM_REPAINT_STEP2 strglobe= %s temLen=%d\n", strGlobeSource, temLen);
            if (ii == temLen)
            {
                menu->m.user.nPosUser = 0;
                ii = 0;
            }

            for (int i = 0; i < canfit-1; i ++)
            {
                strTem[i] = strGlobeSource[i+1];
            }

            strTem[canfit-1] = strTemSource[ii];
            strTem[canfit] = '\0';
            strcpy(strGlobeSource, strTem);

            //debugf(" enter ZCOM_REPAINT_STEP2 strglobe= %s\n", strGlobeSource);
        }
        break;
    }

    //debugf(" enter ZCOM_REPAINT_STEP2 strTem= %s\n", strTem);
    Pw_DrawStringAnchor(gc, x, y, strTem, anchor);
//    LastnPos = menu->m.user.nPosUser;

    int nBytes = 0;
    Pw_FontGetUtf8Char(strTem, &nBytes);
    menu->m.user.nPosUser += nBytes;

    //start the task
    pollerTaskTimeoutRel(&uiMenuS.zcom_RepaintScrollStrTask, pollerClockMsecToTicks(ZCOM_REPAINT_INTERVAL));
}

// Tianjun, 2007-6-30 18:29
//
static void zcom_uiMenuTypeListRepaint(
    UIMenu *menu,
    Pw_GC *gc,
    Pw_Coord y0,
    Pw_Coord y1
)
{
    Pw_Coord  w, h, sb_w;
    Pw_WindowGetSize(Pw_GCGetWindow(gc), &w, &h);

    Pw_Font *SmallFont = dispFontGet(DISP_FONT_TYPE_SMALL);
    Pw_Font *RegularFont = dispFontGet(DISP_FONT_TYPE_REGULAR);
    int RegularFontHeight = Pw_FontGetHeight(RegularFont);

    menu->m.list.nDispLines = (y1 - y0) / RegularFontHeight;

    // Check Node Count
    //
    if (menu->m.list.nodeCntCB != NULL)
    {
        menu->m.list.nodeCntCB(
            &menu->currNode,
            &menu->m.list.nNodes,
            menu->m.list.nodeCBArg
            );
    }

    if (menu->m.list.nNodes == 0)
    {
        return;
    }

    // Check if a scroll bar need to be draw
    //
    sb_w = w;
    if (menu->m.list.nNodes > menu->m.list.nDispLines)
    {
        w -= CFG_SCROLL_BAR_WIDTH;
    }

    // Get index of the Highlight Node
    //
    int HighlightNode;
    HighlightNode = menu->currNode;

    // Get the current draw Nodes window
    //
    int StartDisplayNode;

    if (menu->currNode < menu->lastStartNode)
    {
        StartDisplayNode = menu->currNode;
    }
    else if (menu->currNode >= (menu->lastStartNode + menu->m.list.nDispLines))
    {
        StartDisplayNode = menu->currNode - menu->m.list.nDispLines + 1;
    }
    else
    {
        StartDisplayNode = menu->lastStartNode;
    }

    if (StartDisplayNode < 0)
    {
        StartDisplayNode = 0;
    }

    menu->lastStartNode = StartDisplayNode;

    // Draw the Nodes window
    //
    for (int i = 0; i < menu->m.list.nDispLines; i++)
    {
        //
        // Start
        //
        char str[UI_MENU_MAXSTR];
        int xoff = 1;
        int yoff = y0 + i * RegularFontHeight;
        int CurrDisplayNode = StartDisplayNode + i;

        if (CurrDisplayNode >= menu->m.list.nNodes)
        {
            break;
        }


        //
        // Highlight
        //
        if (CurrDisplayNode == HighlightNode)
        {
            Pw_GCSetColor(gc, pw_black_pixel);
            Pw_FillRect(gc, 0, yoff, w, RegularFontHeight);
            Pw_GCSetColor(gc, pw_white_pixel);
        }
        else
        {
            Pw_GCSetColor(gc, pw_black_pixel);
        }


        //
        // shortcutHints
        //
        int shortcutHints = uiMenuS.ShowShortcutHints && (!menu->m.list.readonly);
        if (shortcutHints)
        {
            spr(str, sizeof(str), "%d", (1 + CurrDisplayNode) % 100);
            Pw_GCSetFont(gc, SmallFont);
            Pw_DrawStringAnchor(
                    gc,
                    xoff,
                    yoff + RegularFontHeight / 2,
                    str,
                    Pw_TextAnchor_MidLeft
                    );
            xoff += RegularFontHeight;
        }


        //
        // configList
        //
        if ((menu->m.list.configList) &&
            (menu->m.list.configNode == CurrDisplayNode))
        {
            spr(str, sizeof(str), "*");
            Pw_GCSetFont(gc, SmallFont);
            Pw_DrawStringAnchor(
                    gc,
                    xoff,
                    yoff + RegularFontHeight / 2,
                    str,
                    Pw_TextAnchor_MidLeft
                    );
            xoff += RegularFontHeight / 2;
        }


        //
        // string nodeText
        //
        Pw_GCSetFont(gc, RegularFont);
        if (menu->m.list.nodeTextCB)
        {
            menu->m.list.nodeTextCB(
                str,
                sizeof(str),
                CurrDisplayNode,
                menu->m.list.nodeCBArg
                );
        }
        else
        {
            uiMenuTextGet(
                &menu->m.list.nodes[CurrDisplayNode].text,
                str,
                sizeof(str)
                );
        }

        int HaveWidth = w - xoff;
        int FitBytes = Pw_GCStringFit(gc, str, HaveWidth);
        if (FitBytes < strlen(str))
        {
            if (CurrDisplayNode == HighlightNode)
            {
                zcom_DrawScrollingStringForList(
                    gc,
                    xoff,
                    yoff,
                    str,
                    Pw_TextAnchor_TopLeft,
                    HaveWidth
                    );
            }
            else
            {
                char* strPrev = unicodeUtf8PrevChar(&str[FitBytes], str);
                *strPrev = '.';
                *strPrev++ = '.';
                *strPrev++ = '\0';
                Pw_DrawStringAnchor(
                    gc,
                    xoff,
                    yoff,
                    str,
                    Pw_TextAnchor_TopLeft
                    );
            }
        }
        else
        {
            Pw_DrawStringAnchor(
                gc,
                xoff,
                yoff,
                str,
                Pw_TextAnchor_TopLeft
                );
        }


        //
        // The End
        //
    }

    // Check if a scroll bar need to be draw
    //
    if (menu->m.list.nNodes > menu->m.list.nDispLines)
    {
        if (menu->m.list.readonly)
        {
            int npages = 1 + (menu->m.list.nNodes - 1) / menu->m.list.nDispLines;
            int page = menu->currNode / menu->m.list.nDispLines;
            uiMenuScrollBarDraw(
                gc,
                sb_w,
                y0, y1,
                page,
                npages
                );
        }
        else
        {
            uiMenuScrollBarDraw(
                gc,
                sb_w,
                y0, y1,
                menu->currNode,
                menu->m.list.nNodes
                );
        }
    }
}

static void zcom_uiMenuDrawInputMethodSequencePrompt(Pw_GC *gc, UIMenu* menu, Pw_Coord y0, Pw_Coord w)
{
    UnicodeChar SequencePromptUnicodeStr[ZCOM_INPUT_METHOD_CUSTOM_SEQUENCE_NUM_MAX + 1] = {};
    char Utf8Str[(ZCOM_INPUT_METHOD_CUSTOM_SEQUENCE_NUM_MAX + 1) * UNICODE_CHAR_BYTES_MAX] = {};
    int SequencePromptLen = 0;

    for (int SeqState = 0;;)
    {
        SequencePromptUnicodeStr[SeqState] = langCharSeq(
            menu->m.text.lastKeySym,
            g_ZComInputMethodCustom[s_ZComInputMethodState.ZComInputMethodTypeCurrent].InputMethodIndex,
            (unsigned char*)&SeqState
            );
        SequencePromptLen++;
        if (SeqState == 0)
        {
            break;
        }
    }

    unicodeStrUni2Utf8(Utf8Str, sizeof(Utf8Str), SequencePromptUnicodeStr);

    Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));
    int FontHeight = Pw_GCGetFontHeight(gc);

    Pw_GCSetColor(gc, pw_black_pixel);
    Pw_DrawStringAnchor(
        gc,
        w,
        y0,
        Utf8Str,
        Pw_TextAnchor_TopRight);

    //
    // Highlight display current selected character
    //
    char HighlightUtf8Str[2 * UNICODE_CHAR_BYTES_MAX] = {};
    int HighlightSeq = (menu->m.text.seqState == 0)?
        (SequencePromptLen - 1):(menu->m.text.seqState - 1);

    memset(Utf8Str, 0 ,sizeof(Utf8Str));
    unicodeStrUni2Utf8(Utf8Str, sizeof(Utf8Str), &SequencePromptUnicodeStr[HighlightSeq]);
    int HighlightStrX = w - Pw_GCGetStringWidth(gc, Utf8Str);

    unicodeChar2Utf8(
        HighlightUtf8Str,
        UNICODE_CHAR_BYTES_MAX,
        SequencePromptUnicodeStr[HighlightSeq]
    );

    Pw_FillRect(
        gc,
        HighlightStrX,
        y0,
        Pw_GCGetStringWidth(gc, HighlightUtf8Str),
        FontHeight
        );

    Pw_GCSetColor(gc, pw_white_pixel);
    Pw_DrawStringAnchor(
        gc,
        HighlightStrX,
        y0,
        HighlightUtf8Str,
        Pw_TextAnchor_TopLeft
        );

    //
    // Draw space symbol
    //
    for (int i = 0;i < SequencePromptLen;i++)
    {
        if (SequencePromptUnicodeStr[i] == ' ')
        {
            memset(Utf8Str, 0 ,sizeof(Utf8Str));
            unicodeStrUni2Utf8(Utf8Str, sizeof(Utf8Str), &SequencePromptUnicodeStr[i]);
            int SpaceStrX = w - Pw_GCGetStringWidth(gc, Utf8Str);
            char SpaceUtf8Str[2 * UNICODE_CHAR_BYTES_MAX] = {};

            unicodeChar2Utf8(
                SpaceUtf8Str,
                UNICODE_CHAR_BYTES_MAX,
                SequencePromptUnicodeStr[i]
            );

            unsigned char bIsSpaceSelected = (SequencePromptUnicodeStr[HighlightSeq] == ' ');

            Pw_GCSetColor(gc, bIsSpaceSelected?pw_black_pixel:pw_white_pixel);
            Pw_FillRect(
                gc,
                SpaceStrX,
                y0,
                Pw_GCGetStringWidth(gc, SpaceUtf8Str),
                FontHeight
                );
            Pw_GCSetColor(gc, bIsSpaceSelected?pw_white_pixel:pw_black_pixel);
            Pw_DrawLine(
                gc,
                SpaceStrX + 1,
                y0 + FontHeight - 2,
                w - 1,
                y0 + FontHeight - 2
                );
            Pw_DrawLine(
                gc,
                w - 1,
                y0 + FontHeight - 2,
                w - 1,
                y0 + 2 * FontHeight / 3
                );
            Pw_DrawLine(
                gc,
                SpaceStrX,
                y0 + FontHeight - 2,
                SpaceStrX,
                y0 + 2 * FontHeight / 3
                );

            break;
        }
    }

    //
    // Restore original gc
    //
    Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));
    Pw_GCSetColor(gc, pw_black_pixel);
}

static void uiMenuWinRepaint(Pw_GC *gc)
{
    Pw_Window *win = Pw_GCGetWindow(gc);
    Pw_Coord   w, h, y0 = 0, y1;
    UIMenu     *menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];
    char       str[UI_MENU_MAXSTR];
    Pw_Rectangle r;
    int        i;
    unsigned char bIsTitleCanFitInPhonebook = 1;

    Pw_WindowGetSize(win, &w, &h);

    //
    // Calculate whether title can fit in phonebook or not
    //
    {
        int PhonebookHeight = h;

        Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_SMALL));
        PhonebookHeight -= Pw_GCGetFontHeight(gc);

        Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));
        PhonebookHeight -= (Pw_GCGetFontHeight(gc) + 3);

        if (PhonebookHeight < (4 * Pw_GCGetFontHeight(gc)))
        {
            bIsTitleCanFitInPhonebook = 0;
        }
    }

    Pw_GCSetColor(gc, pw_white_pixel);
    Pw_FillRect(gc, 0, 0, w, h);

    Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_SMALL));
    Pw_GCSetColor(gc, pw_black_pixel);

    if ( menu->type == UI_MENU_TYPE_USER && menu->m.user.softKeyUpdate )
    {
        menu->m.user.softKeyUpdate(menu);
    }

    if ( menu->type == UI_MENU_TYPE_STATUS )
    {
        /* Get an update */
        if ( menu->m.status.updateCB )
        {
             (*menu->m.status.updateCB)(
                menu->m.status.msg,
                sizeof(menu->m.status.msg),
                &menu->m.status.failure,
                &menu->m.status.success
                );
        }

        if ( menu->m.status.failure )
        {
            uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Cancel);
            uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Retry);
        }
        else if ( menu->m.status.success )
        {
            uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Done);
            uiMenuSoftKeyPositive(menu->softKey, 0 /* LANG_SoftKey_Done */); // do niot show. Tianjun, 2007-6-14 9:46
        }
        else
        {        /* in-progress */
            uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Cancel);
            uiMenuSoftKeyPositive(menu->softKey, 0 /* do not show */);
        }
    }

    if ( menu->title.textIdx != LANG_NULL || menu->title.textCB )
    {
        if (!bIsTitleCanFitInPhonebook && (menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY
			|| menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY) ) //by Mike
        {
            //
            // Do not paint phonebook title
            //
        }
        else
        {
            /*
             * Do the menu title
             */
            uiMenuTextGet(&menu->title, str, sizeof(str));

            Pw_DrawStringAnchor(gc, w / 2, 0, str, Pw_TextAnchor_TopMid);
            Pw_GetStringAnchorBBox(gc, w / 2, 0, str, Pw_TextAnchor_TopMid, &r);
            Pw_DrawLine(gc, 0, r.y + r.h / 2, r.x - 3, r.y + r.h / 2);
            Pw_DrawLine(gc, r.x + r.w + 2, r.y + r.h / 2, w - 1, r.y + r.h / 2);

            /*
             * Top row we can use for rendering menu below title
             */
            y0 = r.y + r.h;
        }
    }

    /*
     * Last row we can use for rendering menu above soft keys,
     * current default to bottom of display since soft keys
     * might be disabled.
     */
    y1 = h - 1;

    /*
     * Do the soft keys (if enabled)
     */
    Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));

    /* NOTE! softKey[] arrays are layed out from left to right always! */
    for ( i = 0 ; i < KEYPAD_SOFTKEY_NUM ; i++ ) {
        if ( menu->softKey[i].text.textIdx == 0
                && !menu->softKey[i].text.textCB ) {
            /*
             * Zero text index and CB means soft key is disabled.
             */
            continue;
        }
        uiMenuTextGet(&menu->softKey[i].text, str, sizeof(str));

        /*
         * Lay the soft keys across the bottom of the screen
         */
        #if KEYPAD_SOFTKEY_NUM >= 2
        if ( i == 0 /*== LEFT SIDE*/ ) {
            /*
            * Bottom left
            */
            Pw_DrawStringAnchor(gc, 0, h - 1,
                    str, Pw_TextAnchor_BotLeft);
            Pw_GetStringAnchorBBox(gc, 0, h - 1,
                    str, Pw_TextAnchor_BotLeft, &r);
        }
        else if ( i == KEYPAD_SOFTKEY_NUM-1 /*== RIGHT SIDE*/ ) {
            /*
            * Bottom right
            */
            Pw_DrawStringAnchor(gc, w - 1, h - 1,
                    str, Pw_TextAnchor_BotRight);
            Pw_GetStringAnchorBBox(gc, w - 1, h - 1,
                    str, Pw_TextAnchor_BotRight, &r);
        }
        else
        #endif
        {
            /*
             * Layout along bottom center uniformly (eg: for 4 keys,
             * put bottom center at 3/8 and 5/8 for middle two keys)
             */
            Pw_DrawStringAnchor(gc,
                    ((2 * i + 1) * w) / (2 * KEYPAD_SOFTKEY_NUM),
                    h - 1, str, Pw_TextAnchor_BotMid);
            Pw_GetStringAnchorBBox(gc,
                    ((2 * i + 1) * w) / (2 * KEYPAD_SOFTKEY_NUM),
                    h - 1, str, Pw_TextAnchor_BotMid,
                    &r);
        }
        /*
         * Reserve space for soft keys along the bottom of display
         */
        if ( y1 > r.y ) y1 = r.y;
    }

    switch ( menu->type )
    {
    case UI_MENU_TYPE_LIST:
        {
            zcom_uiMenuTypeListRepaint(menu, gc, y0, y1);
            break;
        }

    case UI_MENU_TYPE_NUM_ENTRY:
    case UI_MENU_TYPE_HEX_ENTRY:
    case UI_MENU_TYPE_TEXT_ENTRY:
    case UI_MENU_TYPE_TEXT_EX_ENTRY: //2007/04/09   lizhen
	case UI_MENU_TYPE_PKEY_TEXT_ENTRY: //by Mike
	case UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY:
        {
            int diff, nLines;

            /*
            * Figure out how large the edit box should be based on the
            * normal sized font and center it vertically in the
            * remaining space.
            */
            nLines = (y1 - y0 - 2) / Pw_GCGetFontHeight(gc);
            diff = (y1 - y0 - 2 - nLines * Pw_GCGetFontHeight(gc)) / 2;
            y0 += diff;
            y1 -= diff;

            if (UI_MENU_TYPE_NUM_ENTRY != menu->type)
            {
                if (menu->m.text.bZComInputMethodTypeInit)
                {
                    s_ZComInputMethodState.ZComInputMethodTypeCurrent = menu->m.text.ZComInputMethodTypeIndex;
                    menu->m.text.bZComInputMethodTypeInit = false;
                }

                //
                // Input method prompt
                //
                const char *PromptStr = langCharPageNameGet(
                    g_ZComInputMethodCustom[s_ZComInputMethodState.ZComInputMethodTypeCurrent].InputMethodIndex);

                Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_SMALL));
                int iPromptHeight = Pw_GCGetFontHeight(gc);
                int iPromptWidth = Pw_GCGetStringWidth(gc, PromptStr);

                Pw_FillRect(
                    gc,
                    w/2 - iPromptWidth / 2 - 2,
                    y1 + 1,
                    iPromptWidth + 4,
                    iPromptHeight
                    );
                Pw_GCSetColor(gc, pw_white_pixel);

                Pw_DrawStringAnchor(
                    gc,
                    w/2 - iPromptWidth / 2,
                    y1 + 1,
                    PromptStr,
                    Pw_TextAnchor_TopLeft);

                //
                // Repaint input method sequence prompt
                //
                if (menu->m.text.lastKeySym != KEYSYM_UNDEF && g_ZComInputMethodSequencePromptNeed != 0)
                {
                    if (!bIsTitleCanFitInPhonebook && (menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY
						 || menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY) ) //by Mike
                    {
                        //
                        // Repaint prompt on bottom of phonebook
                        //
                        Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));
                        Pw_GCSetColor(gc, pw_white_pixel);
                        iPromptHeight = Pw_GCGetFontHeight(gc);
                        Pw_FillRect(gc, 0, h - 1 - iPromptHeight, w, Pw_GCGetFontHeight(gc) + 1);

                        zcom_uiMenuDrawInputMethodSequencePrompt(gc, menu, h - 1 - iPromptHeight, w);
                    }
                    else
                    {
                        // Clear title
                        Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_SMALL));
                        Pw_GCSetColor(gc, pw_white_pixel);
                        Pw_FillRect(gc, 0, 0, w, Pw_GCGetFontHeight(gc));

                        zcom_uiMenuDrawInputMethodSequencePrompt(gc, menu, 0, w);
                    }
                }

                //
                // Restore original gc
                //
                Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));
                Pw_GCSetColor(gc, pw_black_pixel);
            }

            if(menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY)
            {
                Pw_Font *smallfont = dispFontGet(DISP_FONT_TYPE_SMALL);
                Pw_Font *font = dispFontGet(DISP_FONT_TYPE_REGULAR);

                Pw_GCSetFont(gc, font);
                int fontHeight = Pw_GCGetFontHeight(gc);

                uiMenuTextEntryLayout(
                    menu, gc, DISP_FONT_TYPE_REGULAR, 0/*if mustfit*/,
                    y0, y0 + fontHeight + 2, 0, w - 1);

                int sortedid = 0;
                char name[32] = {};
                char number[32] = {};
                int index = 0;
                int nCount = 0;
                int nFlag = 0;

                if(strcmp(menu->m.text.str, menu->m.text.preStr) != 0 || menu->m.text.SearchFlag == 0)
                {
                    nFlag = 1;
                    menu->m.text.SearchFlag = 1;
                    memset(nodeList, 0, sizeof(nodeList));
                    sprcpy(menu->m.text.preStr, sizeof(menu->m.text.preStr), menu->m.text.str);
                }

                while(nFlag && PhoneBook_SearchName("",
                    sortedid, 0, (int)menu->m.text.nodeCBArgEx, &sortedid) != -1)
                {
                    index = PhoneBook_GetIndexBySortedId(sortedid);
                    PhoneBook_RecordGetName(index, name, sizeof(name));
                    PhoneBook_RecordGetNumber(index, number, sizeof(number));

                    sortedid += 1;
                    nodeList[nCount].text.textCB = uiPhoneBook_GetNameCallback;
                    nodeList[nCount].text.textCBarg = (void*)index;

                    nodeList[nCount].selectCB = uiPhoneBook_RecordSelectStart;
                    nodeList[nCount].selectCBarg = (void*)index;

                    nodeList[nCount].talkCB = uiPhoneBook_DialByIndex;
                    nCount++;
                }

                if(nFlag && PhoneBook_SearchName(menu->m.text.str, 0, 0, (int)menu->m.text.nodeCBArgEx, &sortedid) != -1)
                {
                    if(sortedid < PhoneBook_GetCount())
                    {
                        menu->currNode = sortedid;
                    }
                }

                if(nFlag != 0)
                {
                    menu->m.text.nNodes = nCount;
                    menu->m.text.dispTopNode = 0;
                    menu->m.text.readonly = 0;
                }
                menu->m.text.nDispLines  = 3;
                menu->m.text.nodes = nodeList;

                int shortcutHints = uiMenuS.ShowShortcutHints &&
                    !menu->m.text.readonly;

                if ( menu->m.text.nodeCntCB )
                {
                    menu->m.text.nodeCntCB(&menu->currNode,
                        &menu->m.text.nNodes,
                        menu->m.text.nodeCBArg);
                }

                if ( menu->m.text.nNodes == 0 )
                {
                    break;
                }

                /*
                * Determine how many lines in display
                */
                Pw_GCSetFont(gc, font);
                fontHeight = Pw_GCGetFontHeight(gc);    // of regular font
                assert(fontHeight > 0);

                y0 += (fontHeight + 3);

                int nLines = (y1 - y0) / fontHeight;  // no. that can be shown
                if ( 0 < menu->m.text.nDispLines
                    && menu->m.text.nDispLines < nLines )
                {
                    /*
                    * Reduce number of lines according to menu settings
                    */
                    nLines = menu->m.text.nDispLines;
                    if ( nLines <= (y1 - y0) /
                        Pw_FontGetHeight(dispFontGet(DISP_FONT_TYPE_LARGE)) )
                    {
                        /*
                        * Use bigger font since it should fit
                        */
                        font = dispFontGet(DISP_FONT_TYPE_LARGE);
                        Pw_GCSetFont(gc, font);
                        fontHeight = Pw_GCGetFontHeight(gc);
                    }
                }
                menu->m.text.nDispLines = nLines;

                /*
                * Decide if the display will wrap (when there are more nodes
                * than display lines)
                */
                int wrap = 0;       // nonzero if more lines than can be displayed
                if ( nLines < menu->m.text.nNodes )
                {
                    wrap = 1;
                }

                /*
                * Now make sure the current node is in the display range.
                */
                int page = menu->currNode / nLines;
                menu->m.text.dispTopNode = page * nLines;

                /*
                * Render each of the nodes, highlighting the current
                * selection.
                */
                for ( i = 0 ; i < nLines ; i++ )
                {
                    int nodeNum = i + menu->m.text.dispTopNode;

                    if ( nodeNum >= menu->m.text.nNodes )
                    {
                        break;
                    }
                    if ( nodeNum == menu->currNode )
                    {
                        /*
                        * Highlight this entry by reversing the colors
                        */
                        Pw_GCSetColor(gc, pw_black_pixel);
                        Pw_FillRect(gc, 0,     y0 + i * fontHeight,
                            (wrap)?(w - CFG_SCROLL_BAR_WIDTH):w, fontHeight);
                        Pw_GCSetColor(gc, pw_white_pixel);
                    }
                    else
                    {
                        Pw_GCSetColor(gc, pw_black_pixel);
                    }
                    // Determine left margin
                    int xoff = 2; // leave a little space on left always

                    if ( shortcutHints )
                    {
                        if (nodeNum < 9)
                        {
                            xoff += fontHeight/2;   // leave space for hint
                        }
                        else if (nodeNum < 99)
                        {
                            xoff += fontHeight;     // leave space for hint
                        }
                        else
                        {
                            xoff += fontHeight + fontHeight/2;      // leave space for hint
                        }
                    }
                    if ( menu->m.text.configList &&
                        nodeNum == menu->m.text.configNode )
                    {
                        xoff += fontHeight/2;  // leave space for selection marker
                    }
                    // Get the text and determine if we should reduce font
                    // size for this line.
                    if ( menu->m.text.nodes[nodeNum].text.textCB )
                    {
                        menu->m.text.nodes[nodeNum].text.textCB(str, sizeof(str), (LangIdx)NULL, nodeNum,
                            menu->m.text.nodes[nodeNum].text.textCBarg);
                    }
                    else
                    {
                        uiMenuTextGet(&menu->m.text.nodes[nodeNum].text,
                            str, sizeof(str));
                    }
                    int len = strlen(str); //junky open it 2007-4-24
                    int havewidth = w - xoff;
                    if ( wrap )
                    {
                        havewidth -= CFG_SCROLL_BAR_WIDTH;
                    }
                    int canfit = Pw_GCStringFit(gc, str, havewidth); //junky open it 2007-4-24

                    // junky 2007-4-24
                    if (canfit < len)
                    {
                        if (nodeNum == menu->currNode)
                        {
                            zcom_DrawScrollingStringForText(
                                gc,
                                xoff,
                                y0 + i * fontHeight,
                                str,
                                Pw_TextAnchor_TopLeft,
                                havewidth
                                );
                        }
                        else
                        {
                            char* strPrev = unicodeUtf8PrevChar(&str[canfit], str);
                            *strPrev = '.';
                            *strPrev++ = '.';
                            *strPrev++ = '\0';
                            Pw_DrawStringAnchor(
                                gc,
                                xoff,
                                y0 + i * fontHeight,
                                str,
                                Pw_TextAnchor_TopLeft
                                );
                        }
                    }
                    else
                    {
                        Pw_DrawStringAnchor(
                            gc,
                            xoff,
                            y0 + i * fontHeight,
                            str,
                            Pw_TextAnchor_TopLeft
                            );
                    }

                    if ( shortcutHints )
                    {
                        // Tianjun 2007-4-29 11:28
                        char str[4];

                        Pw_GCSetFont(gc, smallfont);

                        /* Put a small digit in front of the text indicating
                        *  the shortcut
                        */
                        if ( nodeNum < 9 )
                        {
                            str[0] = '1' + nodeNum;
                            str[1] = 0;
                            Pw_DrawStringAnchor(
                                gc,
                                1/*x*/,
                                y0 + (i) * fontHeight + fontHeight/2,
                                str,
                                Pw_TextAnchor_MidLeft);
                        }
                        else if ( nodeNum < 99 )
                        {
                            spr(str, sizeof(str), "%2d", (1+nodeNum)%100);
                            Pw_DrawStringAnchor(
                                gc,
                                1/*x*/,
                                y0 + (i) * fontHeight + fontHeight/2,
                                str,
                                Pw_TextAnchor_MidLeft);
                        }
                        else
                        {
                            spr(str, sizeof(str), "%d", 1+nodeNum);
                            Pw_DrawStringAnchor(
                                gc,
                                1/*x*/,
                                y0 + (i) * fontHeight + fontHeight/2,
                                str,
                                Pw_TextAnchor_MidLeft);
                        }
                    }
                    if ( menu->m.text.configList && nodeNum ==
                        menu->m.text.configNode )
                    {
                        /* put special character to indicate selected */
                        str[0] = '*';       /* selection marker */
                        str[1] = 0;

                        int extraxoff = (shortcutHints ?fontHeight : 0);
                        Pw_GCSetFont(gc, smallfont);
                        Pw_DrawStringAnchor(
                            gc,
                            extraxoff + 1/*x*/,
                            y0 + (i) * fontHeight + fontHeight/2,
                            str,
                            Pw_TextAnchor_MidLeft);
                    }

                    Pw_GCSetFont(gc, font);
                }

                /* Can omit the scroll bar if no wrapping.
                * Advantage: less clutter on screen, slightly longer space
                * available for text, and is clear if we are wrapped or not.
                * Disadvantage: could be considered inconsistent.
                */
                if ( wrap )
                {
                    if ( menu->m.text.readonly )
                    {
                        int npages = 1 +
                            (menu->m.text.nNodes-1) / menu->m.text.nDispLines;
                        int page = menu->currNode / menu->m.text.nDispLines;
                        uiMenuScrollBarDraw(
                            gc, w, y0, y1,
                            page, npages );
                    }
                    else
                    {
                        uiMenuScrollBarDraw(
                            gc, w, y0, y1,
                            menu->currNode,
                            menu->m.text.nNodes
                            );
                    }
                }
            }
			else if(menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY) //by mike
			{
				Pw_Font *smallfont = dispFontGet(DISP_FONT_TYPE_SMALL);
                Pw_Font *font = dispFontGet(DISP_FONT_TYPE_REGULAR);

                Pw_GCSetFont(gc, font);
                int fontHeight = Pw_GCGetFontHeight(gc);

                uiMenuTextEntryLayout(
                    menu, gc, DISP_FONT_TYPE_REGULAR, 0/*if mustfit*/,
                    y0, y0 + fontHeight + 2, 0, w - 1);

                int sortedid = 0;
                char name[32] = {};
                char number[32] = {};
                int index = 0;
                int nCount = 0;
                int nFlag = 0;

                if(strcmp(menu->m.text.str, menu->m.text.preStr) != 0 || menu->m.text.SearchFlag == 0)
                {
                    nFlag = 1;
                    menu->m.text.SearchFlag = 1;
                    memset(nodeList, 0, sizeof(nodeList));
                    sprcpy(menu->m.text.preStr, sizeof(menu->m.text.preStr), menu->m.text.str);
                }

				int mode = 0;
				//May be better sort in number, here just use name as usual
				//by Mike
                while(nFlag && PhoneBook_SearchName("",
                    sortedid, 0, (int)menu->m.text.nodeCBArgEx, &sortedid) != -1)
                {
                    index = PhoneBook_GetIndexBySortedId(sortedid);
                    PhoneBook_RecordGetName(index, name, sizeof(name));
                    PhoneBook_RecordGetNumber(index, number, sizeof(number));

                    sortedid += 1;

					//skip P2P mode
					if(!menu->m.text.showPBookP2P)
					{
						if(PhoneBook_RecordGetMode(index, &mode)<0)
							break;
						if(mode!=0)
							continue;
					}
					_cbPara[nCount].menu = menu;
					_cbPara[nCount].pbIndex = index;

                    nodeList[nCount].text.textCB = uiPhoneBook_GetNameCallback;
                    nodeList[nCount].text.textCBarg = (void*)index;

                    nodeList[nCount].selectCB = _PBook_SelectCB;
                    nodeList[nCount].selectCBarg = (void*)&_cbPara[nCount];

                    nCount++;
                }

                if(nFlag && PhoneBook_SearchName(menu->m.text.str, 0, 0, (int)menu->m.text.nodeCBArgEx, &sortedid) != -1)
                {
                    if(sortedid < PhoneBook_GetCount())
                    {
                        menu->currNode = sortedid;
                    }
                }

                if(nFlag != 0)
                {
                    menu->m.text.nNodes = nCount;
                    menu->m.text.dispTopNode = 0;
                    menu->m.text.readonly = 0;
                }
                menu->m.text.nDispLines  = 3;
                menu->m.text.nodes = nodeList;

                int shortcutHints = uiMenuS.ShowShortcutHints &&
                    !menu->m.text.readonly;

                if ( menu->m.text.nodeCntCB )
                {
                    menu->m.text.nodeCntCB(&menu->currNode,
                        &menu->m.text.nNodes,
                        menu->m.text.nodeCBArg);
                }

                if ( menu->m.text.nNodes == 0 )
                {
                    break;
                }

                /*
                * Determine how many lines in display
                */
                Pw_GCSetFont(gc, font);
                fontHeight = Pw_GCGetFontHeight(gc);    // of regular font
                assert(fontHeight > 0);

                y0 += (fontHeight + 3);

                int nLines = (y1 - y0) / fontHeight;  // no. that can be shown
                if ( 0 < menu->m.text.nDispLines
                    && menu->m.text.nDispLines < nLines )
                {
                    /*
                    * Reduce number of lines according to menu settings
                    */
                    nLines = menu->m.text.nDispLines;
                    if ( nLines <= (y1 - y0) /
                        Pw_FontGetHeight(dispFontGet(DISP_FONT_TYPE_LARGE)) )
                    {
                        /*
                        * Use bigger font since it should fit
                        */
                        font = dispFontGet(DISP_FONT_TYPE_LARGE);
                        Pw_GCSetFont(gc, font);
                        fontHeight = Pw_GCGetFontHeight(gc);
                    }
                }
                menu->m.text.nDispLines = nLines;

                /*
                * Decide if the display will wrap (when there are more nodes
                * than display lines)
                */
                int wrap = 0;       // nonzero if more lines than can be displayed
                if ( nLines < menu->m.text.nNodes )
                {
                    wrap = 1;
                }

                /*
                * Now make sure the current node is in the display range.
                */
                int page = menu->currNode / nLines;
                menu->m.text.dispTopNode = page * nLines;

                /*
                * Render each of the nodes, highlighting the current
                * selection.
                */
                for ( i = 0 ; i < nLines ; i++ )
                {
                    int nodeNum = i + menu->m.text.dispTopNode;

                    if ( nodeNum >= menu->m.text.nNodes )
                    {
                        break;
                    }
                    if ( nodeNum == menu->currNode )
                    {
                        /*
                        * Highlight this entry by reversing the colors
                        */
                        Pw_GCSetColor(gc, pw_black_pixel);
                        Pw_FillRect(gc, 0,     y0 + i * fontHeight,
                            (wrap)?(w - CFG_SCROLL_BAR_WIDTH):w, fontHeight);
                        Pw_GCSetColor(gc, pw_white_pixel);
                    }
                    else
                    {
                        Pw_GCSetColor(gc, pw_black_pixel);
                    }
                    // Determine left margin
                    int xoff = 2; // leave a little space on left always

                    if ( shortcutHints )
                    {
                        if (nodeNum < 9)
                        {
                            xoff += fontHeight/2;   // leave space for hint
                        }
                        else if (nodeNum < 99)
                        {
                            xoff += fontHeight;     // leave space for hint
                        }
                        else
                        {
                            xoff += fontHeight + fontHeight/2;      // leave space for hint
                        }
                    }
                    if ( menu->m.text.configList &&
                        nodeNum == menu->m.text.configNode )
                    {
                        xoff += fontHeight/2;  // leave space for selection marker
                    }
                    // Get the text and determine if we should reduce font
                    // size for this line.
                    if ( menu->m.text.nodes[nodeNum].text.textCB )
                    {
                        menu->m.text.nodes[nodeNum].text.textCB(str, sizeof(str), (LangIdx)NULL, nodeNum,
                            menu->m.text.nodes[nodeNum].text.textCBarg);
                    }
                    else
                    {
                        uiMenuTextGet(&menu->m.text.nodes[nodeNum].text,
                            str, sizeof(str));
                    }
                    int len = strlen(str); //junky open it 2007-4-24
                    int havewidth = w - xoff;
                    if ( wrap )
                    {
                        havewidth -= CFG_SCROLL_BAR_WIDTH;
                    }
                    int canfit = Pw_GCStringFit(gc, str, havewidth); //junky open it 2007-4-24

                    // junky 2007-4-24
                    if (canfit < len)
                    {
                        if (nodeNum == menu->currNode)
                        {
                            zcom_DrawScrollingStringForText(
                                gc,
                                xoff,
                                y0 + i * fontHeight,
                                str,
                                Pw_TextAnchor_TopLeft,
                                havewidth
                                );
                        }
                        else
                        {
                            char* strPrev = unicodeUtf8PrevChar(&str[canfit], str);
                            *strPrev = '.';
                            *strPrev++ = '.';
                            *strPrev++ = '\0';
                            Pw_DrawStringAnchor(
                                gc,
                                xoff,
                                y0 + i * fontHeight,
                                str,
                                Pw_TextAnchor_TopLeft
                                );
                        }
                    }
                    else
                    {
                        Pw_DrawStringAnchor(
                            gc,
                            xoff,
                            y0 + i * fontHeight,
                            str,
                            Pw_TextAnchor_TopLeft
                            );
                    }

                    if ( shortcutHints )
                    {
                        // Tianjun 2007-4-29 11:28
                        char str[4];

                        Pw_GCSetFont(gc, smallfont);

                        /* Put a small digit in front of the text indicating
                        *  the shortcut
                        */
                        if ( nodeNum < 9 )
                        {
                            str[0] = '1' + nodeNum;
                            str[1] = 0;
                            Pw_DrawStringAnchor(
                                gc,
                                1/*x*/,
                                y0 + (i) * fontHeight + fontHeight/2,
                                str,
                                Pw_TextAnchor_MidLeft);
                        }
                        else if ( nodeNum < 99 )
                        {
                            spr(str, sizeof(str), "%2d", (1+nodeNum)%100);
                            Pw_DrawStringAnchor(
                                gc,
                                1/*x*/,
                                y0 + (i) * fontHeight + fontHeight/2,
                                str,
                                Pw_TextAnchor_MidLeft);
                        }
                        else
                        {
                            spr(str, sizeof(str), "%d", 1+nodeNum);
                            Pw_DrawStringAnchor(
                                gc,
                                1/*x*/,
                                y0 + (i) * fontHeight + fontHeight/2,
                                str,
                                Pw_TextAnchor_MidLeft);
                        }
                    }
                    if ( menu->m.text.configList && nodeNum ==
                        menu->m.text.configNode )
                    {
                        /* put special character to indicate selected */
                        str[0] = '*';       /* selection marker */
                        str[1] = 0;

                        int extraxoff = (shortcutHints ?fontHeight : 0);
                        Pw_GCSetFont(gc, smallfont);
                        Pw_DrawStringAnchor(
                            gc,
                            extraxoff + 1/*x*/,
                            y0 + (i) * fontHeight + fontHeight/2,
                            str,
                            Pw_TextAnchor_MidLeft);
                    }

                    Pw_GCSetFont(gc, font);
                }

                /* Can omit the scroll bar if no wrapping.
                * Advantage: less clutter on screen, slightly longer space
                * available for text, and is clear if we are wrapped or not.
                * Disadvantage: could be considered inconsistent.
                */
                if ( wrap )
                {
                    if ( menu->m.text.readonly )
                    {
                        int npages = 1 +
                            (menu->m.text.nNodes-1) / menu->m.text.nDispLines;
                        int page = menu->currNode / menu->m.text.nDispLines;
                        uiMenuScrollBarDraw(
                            gc, w, y0, y1,
                            page, npages );
                    }
                    else
                    {
                        uiMenuScrollBarDraw(
                            gc, w, y0, y1,
                            menu->currNode,
                            menu->m.text.nNodes
                            );
                    }
                }
			}
            else
            {
                uiMenuTextEntryLayout(
                    menu, gc, DISP_FONT_TYPE_LARGE, 0/*if mustfit*/,
                    y0, y1, 0, w - 1);
            }

            break;
        }

    case UI_MENU_TYPE_NUMARRAY_ENTRY:
        {
            int diff, i, x0 = 0, x1 = 0, font;
            char str[UI_MENU_MAXSTR], *p;

            /*
            * Render the prompt
            */
            Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));
            Pw_GCSetColor(gc, pw_black_pixel);
            //2007/04/13    lizhen
            uiMenuTextGet(&menu->m.numarray.prompt, str, sizeof(str));
            Pw_DrawStringAnchor(gc, 0, y0, str, Pw_TextAnchor_TopLeft);
            y0 += Pw_GCGetFontHeight(gc);

            /*
            * Try two font sizes, first large then regular
            */
            for ( font = 0 ; font < 2 ; font++ )
            {
                /*
                * Generate the string
                */
                Pw_GCSetFont(gc, dispFontGet(font == 0 ? DISP_FONT_TYPE_LARGE
                    : DISP_FONT_TYPE_REGULAR));
                p = str;
                *p = '\0';
                for ( i = 0 ; i < menu->m.numarray.valN ; i++ )
                {
                    char fmt[20];

                    if ( i == menu->m.numarray.cursor )
                    {
                        x0 = Pw_GCGetStringWidth(gc, str);
                    }
                    if ( menu->m.numarray.leadZ[i] )
                    {
                        spr(fmt, sizeof(fmt), "%%0%dd",
                            menu->m.numarray.leadZ[i]);
                    }
                    else
                    {
                        sprcpy(fmt, sizeof(fmt), "%d");
                    }
                    spr(p, sizeof(str)-(p-str), fmt, menu->m.numarray.val[i]);
                    p += strlen(p);
                    if ( i == menu->m.numarray.cursor )
                    {
                        x1 = Pw_GCGetStringWidth(gc, str);
                    }
                    if ( menu->m.numarray.sep[i] )
                    {
                        *p++ = menu->m.numarray.sep[i];
                        *p   = '\0';
                    }
                }
                if ( font >= 1 || Pw_GCGetStringWidth(gc, str) < w - 3 )
                {
                    break;
                }
            }

            //{{IP_CONTROL
            // tianjun, 2007-05-17
            if ((menu->m.numarray.valN == 4) &&
                (menu->m.numarray.sep[0] == '.') &&
                (menu->m.numarray.sep[1] == '.') &&
                (menu->m.numarray.sep[2] == '.'))
            {
                Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));

                p = &str[0];

                *p = '\0';

                for (i = 0; i < menu->m.numarray.valN; i++)
                {
                    if (i == menu->m.numarray.cursor)
                    {
                        x0 = Pw_GCGetStringWidth(gc, str);
                    }

                    spr(p, sizeof(str)-(p-str), "%3d", menu->m.numarray.val[i]);
                    p += strlen(p);

                    if (i == menu->m.numarray.cursor)
                    {
                        x1 = Pw_GCGetStringWidth(gc, str);
                    }

                    if (menu->m.numarray.sep[i])
                    {
                        *p++ = menu->m.numarray.sep[i];
                    }

                    *p   = '\0';
                }
            }
            //}}IP_CONTROL
            /*
            * The edit box will be a single line.  We assume that
            * the large font will be ok.  Center the box.
            */
            diff = (y1 - y0 - 2 - Pw_GCGetFontHeight(gc)) / 2;
            y0 += diff;
            y1 -= diff;

            /*
            * Now render the text edit area
            */
            Pw_GCSetColor(gc, pw_black_pixel);
            Pw_DrawRect(gc, 0, y0, w - 1, y1 - y0);
            y0++;

            Pw_DrawStringAnchor(gc, 2, y0, str, Pw_TextAnchor_TopLeft);
            if ( x1 > 0 ) {
                int dy = Pw_GCGetFontAscent(gc) + 1;
                Pw_DrawLine(gc, x0 + 2, y0 + dy, x1 + 1, y0 + dy);
            }
            break;
        }

    case UI_MENU_TYPE_SCALE:
        {
            Pw_Coord diff, x0 = 2, w0;

            /*
            * Render the prompt
            */
            Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));
            Pw_GCSetColor(gc, pw_black_pixel);
            uiMenuTextGet(&menu->m.scale.prompt, str, sizeof(str));

            Pw_DrawStringAnchor(gc, 0, y0, str, Pw_TextAnchor_TopLeft);
            y0 += Pw_GCGetFontHeight(gc);

            /*
            * Leave room for the text marker
            */
            y0 += Pw_GCGetFontHeight(gc);

            /*
            * Graphic is up to 13 pixels high, at the midpoint of [y0,y1].
            */
            diff = (y1 - y0 - 13) / 2;
            if ( diff > 0 )
            {
                y0 += diff;
                y1 -= diff;
            }
            w -= 3;
            Pw_DrawRect(gc, x0, y0-4, w, y1 - y0);

            /*
            * Compute the bar width
            */
            x0 += 2;
            w0 = w;
            w0 -= 4;

            if ( menu->m.scale.max != menu->m.scale.min )
            {
                w0 = w0 * (menu->m.scale.curr - menu->m.scale.min)
                    / (menu->m.scale.max  - menu->m.scale.min);
            }

            /*
            * Draw the text value at (x0 + w0, y0)
            */
            if ( menu->m.scale.showValue )
            {
                if ( menu->m.scale.valueCB )
                {
                    (*menu->m.scale.valueCB)(
                        menu->m.scale.curr,
                        menu->m.scale.min,
                        menu->m.scale.max);
                }
                else
                {
                    spr(str, sizeof(str), "%d", menu->m.scale.curr);
                }
                Pw_GetStringAnchorBBox(gc, x0 + w0, y0, str,
                    Pw_TextAnchor_BotMid, &r);
                if ( r.x < x0 )
                {
                    /*
                    * Text is too far to the left - anchor at bottom left
                    */
                    Pw_DrawStringAnchor(gc, x0, y0, str,
                        Pw_TextAnchor_BotLeft);
                }
                else if ( r.x + r.w >= x0 + w - 2 )
                {
                    /*
                    * Text is too far to the right - anchor at bottom right
                    */
                    Pw_DrawStringAnchor(gc, x0 + w - 2, y0, str,
                        Pw_TextAnchor_BotRight);
                }
                else
                {
                    /*
                    * Text is ok - anchor at bottom middle
                    */
                    Pw_DrawStringAnchor(gc, x0 + w0, y0, str,
                        Pw_TextAnchor_BotMid);
                }
            }

            /*
            * Draw the filled in bar.  Always draw at least a small bar.
            */
            if ( w0 == 0 )
            {
                w0 = 1;
            }

            if ( w0 > 0 )
            {
                y0 += 2;
                y1 -= 2;

                Pw_FillRect(gc, x0, y0-4, w0, y1 - y0);
            }

            break;
        }

    case UI_MENU_TYPE_DIALOG:
        {
            int diff, nLines;

            Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));

            /*
            * Figure out how large the box should be based on the
            * normal sized font and center it vertically in the
            * remaining space.
            */
            nLines = (y1 - y0) / Pw_GCGetFontHeight(gc);
            diff = (y1 - y0 - nLines * Pw_GCGetFontHeight(gc)) / 2;
            y0 += diff;
            y1 -= diff;

            /*
            * Try to layout with the larger font.  If that doesn't work
            * then switch to the regular font or the small font.
            */
            char str[2*UI_MENU_MAXSTR]; /* text to display (may be long) */
            if ( menu->m.dialog.str[0] )
            {
                sprcpy(str, sizeof(str), menu->m.dialog.str);
            }
            else
            {
                uiMenuTextGet(&menu->m.dialog.prompt, str, sizeof(str));
            }

            if (menu->m.dialog.isUsedForPing)
            {
                uiMenuDialogTextLayout(
                    str, gc, DISP_FONT_TYPE_REGULAR, 0/*mustfit*/,
                    y0, y1, 0, w - 1, 0);
            }
            else
            {
                uiMenuDialogTextLayout(
                    str, gc, DISP_FONT_TYPE_REGULAR, 1/*mustfit*/,
                    y0, y1, 0, w - 1, 0) &&
                uiMenuDialogTextLayout(
                    str, gc, DISP_FONT_TYPE_SMALL, 0/*mustfit*/,
                    y0, y1, 0, w - 1, 0);
            }

            break;
        }

    case UI_MENU_TYPE_USER:
        if ( menu->m.user.repaintHandler )
        {
            menu->m.user.repaintHandler(menu, gc, y0, y1);
        }
        break;
    case UI_MENU_TYPE_SYMBOL_SELECTION:
        {
            int iFontHeight = 0, iBytes = 0, i = 0, iStrLen = 0;
            Pw_Coord yFirstLine = 0;
            const char *str = menu->m.symbol_selection.str;

            Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));
            iFontHeight =  Pw_GCGetFontHeight(gc);

            Pw_GCSetColor(gc, pw_black_pixel);
            Pw_DrawRect(gc, 0, y0, w, y1 - y0);
            y0++;
            yFirstLine = y0;

            for ( i = 0 ; str[i] ; )
            {
                //
                // Figure out how many characters will fit on this line
                //
                iStrLen = strlen(&str[i]);
                iBytes = (iStrLen > SYMBOL_SELECTION_PER_LINE_SIZE)?SYMBOL_SELECTION_PER_LINE_SIZE:iStrLen;
                if ( iBytes <= 0 )
                {
                    break;
                }

                //
                // Display this line
                //
                Pw_DrawStringNAnchor(gc, 2, y0,
                    str+i, iBytes, Pw_TextAnchor_TopLeft);

                y0 += iFontHeight;
                i += iBytes;
            }

            //
            // Highlight selection
            //

            //
            // Figure out cursor line and column, index start from 0
            //
            int iCursorLine = menu->m.symbol_selection.cursorPos / SYMBOL_SELECTION_PER_LINE_SIZE;
            int iCursorCol = (menu->m.symbol_selection.cursorPos) % SYMBOL_SELECTION_PER_LINE_SIZE;
            char xoffStr[SYMBOL_SELECTION_PER_LINE_SIZE + 1] = {};
            char HighlightChar[2] = {};

            memcpy(
                xoffStr,
                &str[iCursorLine * SYMBOL_SELECTION_PER_LINE_SIZE],
                iCursorCol
                );

            HighlightChar[0] = str[menu->m.symbol_selection.cursorPos];

            int xoffWidth = Pw_GCGetStringWidth(gc, xoffStr);
            int HighlightWidth = Pw_GCGetStringWidth(gc, HighlightChar);

            Pw_GCSetColor(gc, pw_black_pixel);
            Pw_FillRect(
                gc,
                2 + xoffWidth,
                yFirstLine + iCursorLine * iFontHeight,
                HighlightWidth,
                iFontHeight
                );
            Pw_GCSetColor(gc, pw_white_pixel);

            Pw_DrawStringAnchor(
                gc,
                2 + xoffWidth,
                yFirstLine + iCursorLine * iFontHeight,
                HighlightChar,
                Pw_TextAnchor_TopLeft
                );

            //
            // Draw space symbol
            //
            memset(xoffStr, 0, sizeof(xoffStr));
            memcpy(
                xoffStr,
                str,
                SYMBOL_SELECTION_SPACE_INDEX
                );

            xoffWidth = Pw_GCGetStringWidth(gc, xoffStr);
            HighlightWidth = Pw_GCGetStringWidth(gc, " ");

            unsigned char bIsSpaceSelected = (SYMBOL_SELECTION_SPACE_INDEX == menu->m.symbol_selection.cursorPos);
            Pw_GCSetColor(gc, bIsSpaceSelected?pw_black_pixel:pw_white_pixel);
            Pw_FillRect(
                gc,
                2 + xoffWidth,
                yFirstLine,
                HighlightWidth,
                iFontHeight
                );
            Pw_GCSetColor(gc, bIsSpaceSelected?pw_white_pixel:pw_black_pixel);
            Pw_DrawLine(
                gc,
                3 + xoffWidth,
                yFirstLine + iFontHeight -2,
                xoffWidth + HighlightWidth,
                yFirstLine + iFontHeight -2
                );
            Pw_DrawLine(
                gc,
                3 + xoffWidth,
                yFirstLine + iFontHeight -2,
                3 + xoffWidth,
                yFirstLine + 2 * iFontHeight /3
                );
            Pw_DrawLine(
                gc,
                xoffWidth + HighlightWidth,
                yFirstLine + iFontHeight -2,
                xoffWidth + HighlightWidth,
                yFirstLine + 2 * iFontHeight /3
                );

            break;
        }


    case UI_MENU_TYPE_STATUS:
        {
            int diff, nLines;

            Pw_GCSetFont(gc, dispFontGet(DISP_FONT_TYPE_REGULAR));

            /*
            * Figure out how large the box should be based on the
            * normal sized font and center it vertically in the
            * remaining space.
            */
            nLines = (y1 - y0) / Pw_GCGetFontHeight(gc);
            diff = (y1 - y0 - nLines * Pw_GCGetFontHeight(gc)) / 2;
            y0 += diff;
            y1 -= diff;

            /*
            * Try to layout with the larger font.  If that doesn't work
            * then switch to the regular font or the small font.
            */
            char *str = menu->m.status.msg;
            uiMenuDialogTextLayoutBiggestFont(
                str, gc, y0, y1, 0, w - 1, 0);
            break;
        }

    default:
        debugf("uiMenuWinRepaint: Unknown menu type %d\n", menu->type);
        break;
    }
}


static UnicodeChar uiMenuGetNextCharSeq(
    UIMenu *menu,
    int keycode
    )
{
    UnicodeChar uniChar = '?';

    if ( menu->type == UI_MENU_TYPE_NUM_ENTRY )
    {
        if ( keycode >= KEYSYM_1 && keycode <= KEYSYM_9 )
        {
            uniChar = '1' + keycode - KEYSYM_1;
        }
        else if ( keycode == KEYSYM_0 )
        {
            uniChar = '0';
        }
        else if ( keycode == KEYSYM_POUND)
        {
            uniChar = '#';
        }
        else if ( keycode == KEYSYM_STAR )
        {
            uniChar = '*';
        }
    }
    else if ( menu->type == UI_MENU_TYPE_HEX_ENTRY
        || menu->type == UI_MENU_TYPE_TEXT_ENTRY
        || menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY
		|| menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY //by Mike
		|| menu->type == UI_MENU_TYPE_PKEY_TEXT_ENTRY) //by Mike
    {
        uniChar = langCharSeq(
            keycode,
            g_ZComInputMethodCustom[s_ZComInputMethodState.ZComInputMethodTypeCurrent].InputMethodIndex,
            &menu->m.text.seqState);
    }

    return uniChar;
}

//by Mike
static char Pre_testing_str[] = "*#41x#"; //0~3

void uiMenuKeyEvent(
    int press,              /* 0 = release, 1 = press */
    int keycode,            /* see keypad.h */
    int keyRepeat           /* 0 = normal, 1 = auto-repeat generated */
    )
{
    /* For historical reasons, event info is passed along using
    *  the data structure from the pw system.
    */
    Pw_Event pwevent = {
        .key = {
            .data = {
                .type =
                (press ? Pw_KeyPressEventType: Pw_KeyReleaseEventType ),
                .win = &uiMenuS.MenuWin,
            },
            .keycode = keycode,
        },
    };
    Pw_Event *ev = &pwevent;
    UIMenu      *menu = NULL;

    if ( uiMenuS.MenuTop >= 0 )
        menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];
    /*
     * The only events we are interested in for now are keypress/release
     * events.  Punt on everything (anything?) else.
     */
    if ( press != 1 && press != 0 ) {
        return;
    }

    if (!zcom_callCtrlHaveIncomingOrOutgoingCall())
    {
        if (zcom_uiKeypadLockKeyEvent(press, keycode, keyRepeat) == false)
        {
            if (press == 1)
            {
                uiKeyClick(1, keycode, 0);
            }
            else
            {
                uiKeyClick(0, keycode, 0);
            }

            return;
        }
    }

    // Tianjun, 2007-10-23 20:17:00
    if ( menu->type == UI_MENU_TYPE_NUM_ENTRY )
    {
        if ( press == 0 ) // key release event
        {
            pollerTaskStop(&menu->m.text.zeroKeyTimeoutTask);
        }
        else if ( keycode == KEYSYM_0 )
        {
            pollerTaskTimeoutRel(
                &menu->m.text.zeroKeyTimeoutTask,
                pollerClockMsecToTicks(CFG_MENU_TEXT_ENTRY_SCROLL_TIMEOUT_MS)
                );
        }
    }

    /* here for press or release event */
    if ( press == 0 )
    {
        /* here for release event */

        if (keycode == KEYSYM_STAR)
        {
            zcom_WaitStarKeyReleased = false;
        }

        if (keycode == KEYSYM_POUND)
        {
            zcom_WaitPoundKeyReleased = false;
        }

        /* CENTRAL PLACE FOR POWER KEY RELEASE HANDLING */
        if ( keycode == KEYSYM_POWER )
        {
            uiPowerKeyRelease();
            zcom_uiKeyPadLockS.zcom_WaitPowerKeyReleased = false;
            return;
        }
#if KEYPAD_POWER_ALSO_END
        else if ( keycode == KEYSYM_END )
        {
            uiPowerKeyRelease();
            zcom_uiKeyPadLockS.zcom_WaitPowerKeyReleased = false;
        }
#endif

        if (keycode == KEYSYM_SOFT_NEGATIVE &&
            (menu->type == UI_MENU_TYPE_NUM_ENTRY ||
            menu->type == UI_MENU_TYPE_HEX_ENTRY ||
            menu->type == UI_MENU_TYPE_TEXT_ENTRY ||
            menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY ||	//2007/04/09  lizhen
			menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY || //by Mike
			menu->type == UI_MENU_TYPE_PKEY_TEXT_ENTRY))	//by Mike
        {
            //
            // Key clear release
            //
            pollerTaskStop(&ClearButtonTask);
        }

        uiKeyClick( 0/*release*/, keycode, 0 );

        if ( uiMenuS.MenuTop >= 0 )
        {
            menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];
        }
        switch ( menu->type )
        {
        case UI_MENU_TYPE_USER:
            if ( menu->m.user.eventHandler )
            {
                if (menu->m.user.eventHandler(menu, ev)) // Tianjun, 2007-5-8 17:25
                {
                    debugf("Key Release Event is already handled");
                    return;
                }
            }
            break;
            /* For now, don't pass release event to any other type. */
        default:
            break;
        }

        if (keycode == KEYSYM_END)
        {
            switch(menu->type)
            {
            case UI_MENU_TYPE_LIST:
            case UI_MENU_TYPE_TEXT_EX_ENTRY:
            case UI_MENU_TYPE_NUM_ENTRY:
            case UI_MENU_TYPE_HEX_ENTRY:
            case UI_MENU_TYPE_TEXT_ENTRY:
            case UI_MENU_TYPE_NUMARRAY_ENTRY:
            case UI_MENU_TYPE_DIALOG:
			case UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY: //by Mike
			case UI_MENU_TYPE_PKEY_TEXT_ENTRY: //by Mike
                break;
            case UI_MENU_TYPE_SCALE:
                if ( menu->m.scale.doneCB )
                {
                    menu->m.scale.doneCB(menu->m.scale.doneCBarg, menu->m.scale.curr);
                }
                break;
            case UI_MENU_TYPE_SYMBOL_SELECTION:
                s_ZComInputMethodState.ZComInputMethodTypeCurrent =
                    s_ZComInputMethodState.ZComInputMethodTypeLastInUsed;
                break;
            default:
                break;
            }

            uiMainWinKeyEnd();
            return;
        }
        return;
    }

    /* here for press event */
    if (uiMenuLevelGet() == 0 && keycode == KEYSYM_STAR)
    {
        zcom_WaitStarKeyReleased = true;
        pollerTaskTimeoutRel(
            &zcom_HoldStarKeyTask,
            pollerClockMsecToTicks(ZCOM_HOLD_STAR_KEY_MSECS));
    }

    if (zcom_uiModelCapabilityIsEnabled(eModelCapability_PoundKeyEnterP2P))
    {
        if (uiMenuLevelGet() == 0 && keycode == KEYSYM_POUND)
        {
            zcom_WaitPoundKeyReleased = true;
            pollerTaskTimeoutRel(
                &zcom_HoldPoundKeyTask,
                pollerClockMsecToTicks(ZCOM_HOLD_POUND_KEY_MSECS));
        }
    }

    /* CENTRAL PLACE FOR POWER KEY PRESS HANDLING */
    if ( keycode == KEYSYM_POWER )
    {
        uiPowerKeyPress();
        zcom_uiKeyPadLockS.zcom_WaitPowerKeyReleased = true;
    }
#if KEYPAD_POWER_ALSO_END
    else if ( keycode == KEYSYM_END )
    {
        uiPowerKeyPress();
        zcom_uiKeyPadLockS.zcom_WaitPowerKeyReleased = true;
    }
#endif

    uiKeyClick( 1/*press*/, keycode, keyRepeat );

    if ( menu == NULL ) return;

    /* NOTE! softKey[] arrays are layed out from left to right always!
     *  0 == left, NUM-1 == right .
     *  Negative positions indicate key is invalid.
     */
    int softPos = -1;   /* soft key position, left to right; negative invalid*/
    switch( keycode )
    {
    case KEYSYM_SOFT_POSITIVE :
        softPos = KEYPAD_POSITIVE_SOFTKEY_POSITION;
        break;
    case KEYSYM_SOFT_NEGATIVE  :
        softPos = KEYPAD_NEGATIVE_SOFTKEY_POSITION;
        break;
    default:
        break;
    }

    if ( softPos >= 0 )
    {
        if ( menu->softKey[softPos].selectCB )
        {
            menu->softKey[softPos].selectCB(
                menu->softKey[softPos].selectCBarg);
            return;
        }
    }

    switch ( menu->type )
    {
    case UI_MENU_TYPE_LIST:
        {
            if ( menu->m.list.nodeCntCB )
            {
                menu->m.list.nodeCntCB(&menu->currNode,
                    &menu->m.list.nNodes,
                    menu->m.list.nodeCBArg);
            }
            switch ( keycode )
            {
            case KEYSYM_TALK:
                if( menu->m.list.nodes[menu->currNode].talkCB )
                {
                    menu->m.list.nodes[menu->currNode].talkCB(
                        menu->m.list.nodes[menu->currNode].selectCBarg);
                }
                break;

            case KEYSYM_SOFT_POSITIVE:
            case KEYSYM_RIGHT:
            case KEYSYM_SELECT:
                {
                    if ( menu->m.list.nodeSelectCB )
                    {
                        menu->m.list.nodeSelectCB(menu->currNode, menu->m.list.nodeCBArg);
                    }
                    else if ( menu->m.list.nodes[menu->currNode].selectCB )
                    {
                        menu->m.list.nodes[menu->currNode].selectCB(
                            menu->m.list.nodes[menu->currNode].selectCBarg);
                    }
                    else
                    {
                        if ( menu->m.list.readonly )
                        {
                            uiMenuPop(1);
                        }
                    }
                    break;
                }

            case KEYSYM_UP:
                {
                    /* Beware, menu->currNode is unsigned */
                    int currNode = menu->currNode;
                    if ( menu->m.list.readonly )
                    {
                        /* goto previous display page */
                        int nDispLines = menu->m.list.nDispLines;
                        if ( nDispLines <= 0 ) nDispLines = 1;
                        currNode -= nDispLines;
                    }
                    else
                    {
                        currNode--;
                    }
                    if ( currNode < 0 )
                    {
                        currNode = menu->m.list.nNodes + currNode;
                    }
                    if ( currNode < 0 )
                    {
                        currNode = 0;   /* to be sure */
                    }
                    menu->currNode = currNode;

                    // Tianjun 2007-3-27 13:28
                    menu->m.list.nPosList = 0;
                    menu->m.list.nScrollWaitCount = 0;
                    if ( menu->m.list.nodeSelectChangedCB )
                    {
                        menu->m.list.nodeSelectChangedCB(
                            menu->currNode,
                            menu->m.list.nodeCBArg
                            );
                    }

                    break;
                }

            case KEYSYM_DOWN:
                {
                    /* Beware, menu->currNode is unsigned */
                    int currNode = menu->currNode;
                    int i = 0;

                    if ( menu->m.list.readonly )
                    {
                        /* goto previous display page */
                        int nDispLines = menu->m.list.nDispLines;
                        if ( nDispLines <= 0 )
                        {
                            nDispLines = 1;
                        }
                        currNode += nDispLines;
                    }
                    else
                    {
                        currNode++;
                    }
                    if ( currNode >= menu->m.list.nNodes )
                    {
                        currNode = 0;
                    }
                    menu->currNode = currNode;
                    i = currNode - menu->m.list.dispTopNode;
                    if ( i < 0 )
                    {
                        i += menu->m.list.nNodes;
                    }
                    if ( i >= menu->m.list.nDispLines )
                    {
                        menu->m.list.dispTopNode++;
                        if ( menu->m.list.dispTopNode >= menu->m.list.nNodes )
                        {
                            menu->m.list.dispTopNode = 0;
                        }
                    }

                    // Tianjun 2007-3-27 13:28
                    menu->m.list.nPosList = 0;
                    menu->m.list.nScrollWaitCount = 0;
                    if ( menu->m.list.nodeSelectChangedCB )
                    {
                        menu->m.list.nodeSelectChangedCB(
                            menu->currNode,
                            menu->m.list.nodeCBArg
                            );
                    }

                    break;
                }

            case KEYSYM_CLEAR:
            case KEYSYM_SOFT_NEGATIVE:
                uiMenuPop(1);
                return;

            case KEYSYM_0:
            case KEYSYM_1:
            case KEYSYM_2:
            case KEYSYM_3:
            case KEYSYM_4:
            case KEYSYM_5:
            case KEYSYM_6:
            case KEYSYM_7:
            case KEYSYM_8:
            case KEYSYM_9:
                {
                    int i = 0;

                    if (keycode == KEYSYM_0)
                    {
                        //
                        // key 0 means index 9
                        //
                        i = 9;
                    }
                    else
                    {
                        i = keycode - KEYSYM_1;
                    }

                    if ( menu->m.list.readonly )
                    {
                        /* goto appropriate display page */
                        i = i * menu->m.list.nDispLines;
                    }
                    if ( i < menu->m.list.nNodes )
                    {
                        menu->currNode = i;
                        if ( menu->m.list.nodeSelectCB )
                        {
                            menu->m.list.nodeSelectCB(i,
                                menu->m.list.nodeCBArg);
                        }
                        else if ( menu->m.list.nodes[i].selectCB )
                        {
                            menu->m.list.nodes[i].selectCB(
                                menu->m.list.nodes[i].selectCBarg);
                        }
                    }
                    break;
                }

            default:
                return;
            }
            Pw_WindowRepaint(&uiMenuS.MenuWin);
            return;
        }

    case UI_MENU_TYPE_NUM_ENTRY:
    case UI_MENU_TYPE_HEX_ENTRY:
    case UI_MENU_TYPE_TEXT_ENTRY:
    case UI_MENU_TYPE_TEXT_EX_ENTRY:
	case UI_MENU_TYPE_PKEY_TEXT_ENTRY: //by Mike
	case UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY: //by Mike
        {
            pollerTaskStop(&menu->m.text.keyTimeoutTask);
            switch ( keycode )
            {
            case KEYSYM_TALK:
                menu->m.text.lastKeySym = KEYSYM_UNDEF;

                if (menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY)
                {
                    if ( menu->m.text.nodes[menu->currNode].talkCB )
                    {
                        menu->m.text.nodes[menu->currNode].talkCB(menu->m.text.nodes[menu->currNode].selectCBarg);
                    }
                }
				else if(menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY) //by Mike
				{
					//do nothing
					//do not call doneCB here...

				}
                else
                {
                    if ( menu->m.text.doneCB )
                    {
                        menu->m.text.doneCB(menu->m.text.doneCBarg,
                            menu->m.text.str);
                    }
                }
                /* NOTE "menu" is invalid now */
                return; //break; //tianjun, so we return from here

            case KEYSYM_SOFT_POSITIVE:
            case KEYSYM_SELECT:
                {
                    if (menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY)
                    {
                        if ( menu->m.text.nodeSelectCB )
                        {
                            menu->m.text.nodeSelectCB(menu->currNode, menu->m.text.nodeCBArg);
                        }
                        else if ( menu->m.text.nodes[menu->currNode].selectCB )
                        {
                            menu->m.text.nodes[menu->currNode].selectCB(
                                menu->m.text.nodes[menu->currNode].selectCBarg);
                        }
                        else
                        {
                            uiPhoneBook_RecordSelectStart((void*)-1);

                            if ( menu->m.text.readonly )
                            {
                                uiMenuPop(1);
                            }
                        }
                    }
					else if(menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY) //by mike
					{
						if ( menu->m.text.nodes[menu->currNode].selectCB )
                        {
                            menu->m.text.nodes[menu->currNode].selectCB(
                                menu->m.text.nodes[menu->currNode].selectCBarg);
                        }
                        else
                        {
							//call doneCB here with ""
							if ( menu->m.text.doneCB )
							{
								menu->m.text.doneCB(menu->m.text.doneCBarg,
									"");
							}
                        }
					}
                    else
                    {
                        menu->m.text.lastKeySym = KEYSYM_UNDEF;
                        if ( menu->m.text.doneCB )
                        {
                            menu->m.text.doneCB(menu->m.text.doneCBarg,
                                menu->m.text.str);
                        }
                    }
                }
                /* NOTE "menu" is invalid now */
                return; //break; //tianjun, so we return from here

            case KEYSYM_DOWN:
                {
                    if (menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY
						|| menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY) //by Mike
                    {
                        /* Beware, menu->currNode is unsigned */
                        int currNode = menu->currNode;
                        int i = 0;

                        if ( menu->m.text.readonly )
                        {
                            /* goto previous display page */
                            int nDispLines = menu->m.text.nDispLines;
                            if ( nDispLines <= 0 )
                            {
                                nDispLines = 1;
                            }
                            currNode += nDispLines;
                        }
                        else
                        {
                            currNode++;
                        }

                        if ( currNode >= menu->m.text.nNodes )
                        {
                            currNode = 0;
                        }
                        menu->currNode = currNode;
                        i = currNode - menu->m.text.dispTopNode;

                        if ( i < 0 )
                        {
                            i += menu->m.text.nNodes;
                        }
                        if ( i >= menu->m.text.nDispLines )
                        {
                            menu->m.text.dispTopNode++;
                            if ( menu->m.text.dispTopNode >= menu->m.text.nNodes )
                            {
                                menu->m.text.dispTopNode = 0;
                            }
                        }

                        menu->m.text.nPosText = 0;
                        menu->m.text.nScrollWaitCount = 0;

                        if ( menu->m.text.nodeSelectChangedCB )
                        {
                            menu->m.text.nodeSelectChangedCB(
                                menu->currNode,
                                menu->m.text.nodeCBArg
                                );
                        }
                    }
                    else
                    {
                        const char *p = unicodeUtf8NextChar(
                            &menu->m.text.str[menu->m.text.cursorPos]);
                        menu->m.text.cursorPos = p - menu->m.text.str;
                        menu->m.text.lastKeySym = KEYSYM_UNDEF;

                    }

                    break;
                }

            case KEYSYM_SOFT_NEGATIVE:
                if ( !menu->m.text.str[0])
                {
                    //
                    // String is empty so quit this menu
                    //
                    uiMenuPop(1);
                    return;
                }
                else
                {
                    if ( menu->m.text.cursorPos > 0)
                    {
                        int i = 0;
                        //
                        // Clear one character
                        //
                        const char *p = unicodeUtf8PrevChar(
                            &menu->m.text.str[menu->m.text.cursorPos],
                            &menu->m.text.str[0]);

                        int remove = menu->m.text.cursorPos - (p - menu->m.text.str);

                        for ( i = menu->m.text.cursorPos; menu->m.text.str[i] ; i++ )
                        {
                            menu->m.text.str[i - remove] = menu->m.text.str[i];
                        }

                        menu->m.text.str[i - remove] = '\0';
                        menu->m.text.cursorPos -= remove;
                        menu->m.text.lastKeySym = KEYSYM_UNDEF;

                        Pw_WindowRepaint(&uiMenuS.MenuWin);
                    }

                    if (menu->m.text.str[0])
                    {
                        //
                        // String is not empty still, schedule clear task
                        //
                        pollerTaskCookieSet(&ClearButtonTask, (void*)menu);
                        pollerTaskTimeoutRel(&ClearButtonTask,
                            UI_CLEAR_KEY_PRESS_SECONDS * pollerClockHzGet());
                    }
                }
                break;

            case KEYSYM_UP:
                {
                    if (menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY
						|| menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY) //by Mike
                    {
                        /* Beware, menu->currNode is unsigned */
                        int currNode = menu->currNode;

                        if ( menu->m.text.readonly )
                        {
                            /* goto previous display page */
                            int nDispLines = menu->m.text.nDispLines;
                            if ( nDispLines <= 0 )
                            {
                                nDispLines = 1;
                            }
                            currNode -= nDispLines;
                        }
                        else
                        {
                            currNode--;
                        }

                        if ( currNode < 0 )
                        {
                            currNode = menu->m.text.nNodes + currNode;
                        }

                        if ( currNode < 0 )
                        {
                            currNode = 0;   /* to be sure */
                        }
                        menu->currNode = currNode;

                        menu->m.text.nPosText = 0;
                        menu->m.text.nScrollWaitCount = 0;

                        if ( menu->m.text.nodeSelectChangedCB )
                        {
                            menu->m.text.nodeSelectChangedCB(
                                menu->currNode,
                                menu->m.text.nodeCBArg
                                );
                        }
                    }
                    else
                    {
                        const char *p = unicodeUtf8PrevChar(
                            &menu->m.text.str[menu->m.text.cursorPos],
                            &menu->m.text.str[0]);
                        int remove = menu->m.text.cursorPos
                            - (p - menu->m.text.str);
                        menu->m.text.cursorPos -= remove;
                        menu->m.text.lastKeySym = KEYSYM_UNDEF;
                    }

                    break;
                }

            case KEYSYM_0:
            case KEYSYM_1:
            case KEYSYM_2:
            case KEYSYM_3:
            case KEYSYM_4:
            case KEYSYM_5:
            case KEYSYM_6:
            case KEYSYM_7:
            case KEYSYM_8:
            case KEYSYM_9:
            case KEYSYM_STAR:
            case KEYSYM_POUND:
                {
                    int i = 0, utf8Sz = 0;
                    UnicodeChar uniChar = '?';
                    char utf8[UNICODE_CHAR_BYTES_MAX] = {};

                    if (menu->type == UI_MENU_TYPE_NUM_ENTRY && keycode == KEYSYM_POUND)
                    {
                        //
                        // Number entry, check factory mode firstly
                        //
                        if (0 == memcmp(menu->m.text.str, FACTORYTEST_ENTER_CODE, strlen(FACTORYTEST_ENTER_CODE) - 2) &&
                            menu->m.text.cursorPos == strlen(FACTORYTEST_ENTER_CODE) - 1)
                        {
                            char strFactoryTestEnterCode[7] = {0};
                            sprcpy(strFactoryTestEnterCode, sizeof(strFactoryTestEnterCode), menu->m.text.str);
                            uiMenuPopAll();
                            FactoryTest_Enter(strFactoryTestEnterCode);

                            return;
                        }
                        else if (strlen(menu->m.text.str) == menu->m.text.cursorPos &&
                            MatchSipProfileHiddenOnCharSeq(menu->m.text.str))
                        {
                            uiMenuPopAll();
                            if (zcom_uiModelCapabilityIsEnabled(eModelCapability_SIPProfileHidden))
                            {
                                zcom_uiModelCapabilityDisable(eModelCapability_SIPProfileHidden);
                            }
                            else
                            {
                                zcom_uiModelCapabilityEnable(eModelCapability_SIPProfileHidden);
                            }
                            return;
                        }
                        else if (strlen(menu->m.text.str) == menu->m.text.cursorPos &&
                            MatchG729EnableCharSeq(menu->m.text.str))
                        {
                            uiMenuPopAll();
                            if (zcom_uiModelCapabilityIsEnabled(eModelCapability_G729Enable))
                            {
                                zcom_uiModelCapabilityDisable(eModelCapability_G729Enable);
                            }
                            else
                            {
                                zcom_uiModelCapabilityEnable(eModelCapability_G729Enable);
                            }
                            zcom_SipProfileCodecOrderValidate();
                            return;
                        }
                        //by Mike
                        else if (0 == memcmp(menu->m.text.str, Pre_testing_str, strlen(Pre_testing_str) - 2) &&
                            menu->m.text.cursorPos == strlen(Pre_testing_str) - 1)
                        {
                            //0~3
                            char strPreTestEnterCode = menu->m.text.str[4];
                            if(strPreTestEnterCode>='0' && strPreTestEnterCode<='3')
                            {
                               uiMenuPopAll();
                               PreTest_Enter(strPreTestEnterCode);

                               return;
                            }
                        }
                    }

                    if ( keycode == KEYSYM_POUND && menu->type != UI_MENU_TYPE_NUM_ENTRY)
                    {
                        s_ZComInputMethodState.ZComInputMethodTypeCurrent++;

                        if (s_ZComInputMethodState.ZComInputMethodTypeCurrent >= g_ZComInputMethodCustomGetNumber())
                        {
                            s_ZComInputMethodState.ZComInputMethodTypeCurrent = 0;
                        }

//                        enum langPageEnum CurInputMethodType =
//                            g_ZComInputMethodCustom[s_ZComInputMethodState.ZComInputMethodTypeCurrent].InputMethodType;
//
//                        if (CurInputMethodType == langPage_eSymbols)
//                        {
//                            //
//                            // Show symbol choose dialog
//                            //
//                            uiMenuSymbolSelectionStart();
//                        }

                        menu->m.text.lastKeySym = KEYSYM_UNDEF;
                        break;
                    }

                    if ( keycode == KEYSYM_STAR && menu->type != UI_MENU_TYPE_NUM_ENTRY)
                    {
                        uiMenuSymbolSelectionStart();
                        menu->m.text.lastKeySym = KEYSYM_UNDEF;
                        break;
                    }

                    bool bRoomLack = false;
                    bool bKeyFirstPress = false;
                    enum langPageEnum CurInputMethodType =
                        g_ZComInputMethodCustom[s_ZComInputMethodState.ZComInputMethodTypeCurrent].InputMethodType;

                    if ( menu->m.text.lastKeySym != keycode )
                    {
                        /*
                         * New keypress - insert new character.
                         * First get the new character.
                         */
                        bKeyFirstPress = true;

//                        if (CurInputMethodType != langPage_eSymbols &&
//                            CurInputMethodType != langPage_eReserved)
                        if (CurInputMethodType != langPage_eReserved)
                        {
                            //
                            // Remember current input method
                            //
                            s_ZComInputMethodState.ZComInputMethodTypeLastInUsed = s_ZComInputMethodState.ZComInputMethodTypeCurrent;
                        }

                        menu->m.text.seqState = 0;
                        uniChar = uiMenuGetNextCharSeq(menu, keycode);

                        /*
                         * Convert to utf8
                         */
                        utf8Sz = unicodeChar2Utf8(utf8, sizeof(utf8), uniChar);

                        /*
                         * Make room for utf8Sz bytes in the string
                         * and copy in the new character.
                         */
                        if ( utf8Sz <= 0 ||
                            strlen(menu->m.text.str) + utf8Sz >= menu->m.text.bufSize)
                        {
                            /*
                             * String is too long or char is bad;
                             * no room for more chars
                             */
// Tianjun, 2007-9-18 11:32:22
//                                return;
                            bRoomLack = true;
                        }
                        else
                        {
                            for (i = strlen(menu->m.text.str); i >= menu->m.text.cursorPos; i--)
                            {
                                menu->m.text.str[i + utf8Sz] = menu->m.text.str[i];
                            }

                            memcpy(
                                &menu->m.text.str[menu->m.text.cursorPos],
                                utf8,
                                utf8Sz
                                );
                            menu->m.text.cursorPos += utf8Sz;
                            menu->m.text.lastUniChar = uniChar;
                            menu->m.text.lastUtf8Sz  = utf8Sz;
                        }
                    }
                    else
                    {
                        /*
                         * Repeat keypress - cycle through characters
                         */
                        bKeyFirstPress = false;

                        if (menu->m.text.cursorPos < 1)
                        {
                            break;
                        }
                        uniChar = uiMenuGetNextCharSeq(menu, keycode);

                        /*
                         * Convert to utf8
                         */
                        utf8Sz = unicodeChar2Utf8(utf8, sizeof(utf8), uniChar);

                        /*
                         * Decide if we need to add or delete bytes
                         * in the string.  New char has size utf8Sz
                         * and old string has size menu->m.text.lastUtf8Sz.
                         */
                        if ( utf8Sz <= 0 ||
                            menu->m.text.lastUtf8Sz > menu->m.text.cursorPos ||
                            strlen(menu->m.text.str) + utf8Sz - menu->m.text.lastUtf8Sz >= menu->m.text.bufSize)
                        {
                            /*
                             * String would be too long or char is bad;
                             * no room for new char even when old deleted.
                             */
// Tianjun, 2007-9-18 11:32:22
//                            return;
                            bRoomLack = true;
                        }
                        else
                        {
                            if ( utf8Sz > menu->m.text.lastUtf8Sz )
                            {
                                /*
                                 * Add pad bytes prior to cursor
                                 */
                                int pad = utf8Sz - menu->m.text.lastUtf8Sz;
                                for (i = strlen(menu->m.text.str); i >= menu->m.text.cursorPos; i--)
                                {
                                    menu->m.text.str[i + pad] = menu->m.text.str[i];
                                }
                                menu->m.text.cursorPos += pad;
                            }
                            else if ( utf8Sz < menu->m.text.lastUtf8Sz )
                            {
                                /*
                                 * Remove bytes prior to cursor
                                 */
                                int remove = menu->m.text.lastUtf8Sz - utf8Sz;
                                for (i = menu->m.text.cursorPos; menu->m.text.str[i] ; i++)
                                {
                                    menu->m.text.str[i - remove] = menu->m.text.str[i];
                                }
                                menu->m.text.str[i - remove] = '\0';
                                menu->m.text.cursorPos -= remove;
                            }

                            /*
                             * Copy in the new character
                             */
                            memcpy(
                                &menu->m.text.str[menu->m.text.cursorPos - utf8Sz],
                                utf8,
                                utf8Sz
                                );
                            menu->m.text.lastUniChar = uniChar;
                            menu->m.text.lastUtf8Sz  = utf8Sz;
                        }
                    }

                    /* Test to see whether the next char in sequence
                     * for this key would be different.
                     * If so, then stay at current position in line,
                     * but with timeout; else advance.
                     */
                    if (bKeyFirstPress && bRoomLack)
                    {
                        menu->m.text.lastKeySym = KEYSYM_UNDEF;
                    }
                    else
                    {
                        int saveSeqState = menu->m.text.seqState;
                        (void) uiMenuGetNextCharSeq(menu, keycode);
                        if ( saveSeqState != menu->m.text.seqState )
                        {
                            pollerTaskTimeoutRel(
                                &menu->m.text.keyTimeoutTask,
                                pollerClockMsecToTicks(CFG_MENU_TEXT_ENTRY_SCROLL_TIMEOUT_MS)
                                );
                            menu->m.text.seqState = saveSeqState;
                            menu->m.text.lastKeySym = keycode;
                        }
                        else
                        {
                            menu->m.text.lastKeySym = KEYSYM_UNDEF;
                        }
                    }

                    break;
                }

            default:
                return;
            }

            if ( !menu->m.text.str[0] )
            {
                uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
				if(menu->type == UI_MENU_TYPE_PKEY_TEXT_ENTRY) //by Mike
				{
					uiMenuSoftKeyPositive(menu->softKey, menu->m.text.pkeyTextNilEntryIdx);
				}
            }
            else
            {
                uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Clear);
				if(menu->type == UI_MENU_TYPE_PKEY_TEXT_ENTRY) //by Mike
				{
					uiMenuSoftKeyPositive(menu->softKey, menu->m.text.pkeyTextNotNilEntryIdx);
				}
            }

            Pw_WindowRepaint(&uiMenuS.MenuWin);

            return;
        }

    case UI_MENU_TYPE_NUMARRAY_ENTRY:
        {
            switch ( keycode )
            {
            case KEYSYM_SOFT_POSITIVE:
            case KEYSYM_SELECT:
                if ( menu->m.numarray.doneCB )
                {
                    menu->m.numarray.doneCB(menu->m.numarray.doneCBarg,
                        menu->m.numarray.valN,
                        menu->m.numarray.val);
                }
                /* NOTE "menu" is invalid now */
                break;

            case KEYSYM_DOWN:
            case KEYSYM_RIGHT:
                if ( menu->m.numarray.cursor < menu->m.numarray.valN - 1 )
                {
                    menu->m.numarray.cursor++;
                }
                menu->m.numarray.edit = 0;
                break;

            case KEYSYM_UP:
            case KEYSYM_LEFT:
                if ( menu->m.numarray.cursor > 0 )
                {
                    menu->m.numarray.cursor--;
                }
                menu->m.numarray.edit = 0;
                break;

            case KEYSYM_CLEAR:
            case KEYSYM_SOFT_NEGATIVE:
                if (menu->m.numarray.bIgnoreBackEvent)
                {
                    break;
                }

                uiMenuPop(1);
                return;

            case KEYSYM_0:
            case KEYSYM_1:
            case KEYSYM_2:
            case KEYSYM_3:
            case KEYSYM_4:
            case KEYSYM_5:
            case KEYSYM_6:
            case KEYSYM_7:
            case KEYSYM_8:
            case KEYSYM_9:
                {
                    unsigned short new = 0;
                    int i = 0;

                    if ( !menu->m.numarray.edit )
                    {
                        menu->m.numarray.edit = 1;
                        menu->m.numarray.val[menu->m.numarray.cursor] = 0;
                    }
                    new = menu->m.numarray.val[menu->m.numarray.cursor];
                    new = new * 10 + keycode - KEYSYM_0;
                    if ( new <= menu->m.numarray.max[menu->m.numarray.cursor] )
                    {
                            menu->m.numarray.val[menu->m.numarray.cursor] = new;
                    }
                    else if ( menu->m.numarray.cursor < menu->m.numarray.valN - 1 )
                    {
                            /*
                            * Overflow on this field - move to the next
                            */
                            menu->m.numarray.cursor++;
                            new = keycode - KEYSYM_0;
                            menu->m.numarray.val[menu->m.numarray.cursor] = new;
                    }
                    /*
                    * If the next key would overflow, then move to
                    * next field and turn editing off
                    */
                    if ( new * 10 > menu->m.numarray.max[menu->m.numarray.cursor] )
                    {
                            if ( menu->m.numarray.cursor < menu->m.numarray.valN - 1 )
                            {
                                    menu->m.numarray.cursor++;
                            }
                            menu->m.numarray.edit = 0;
                    }
                    /*
                    * Ensure all the non-edited fields are between
                    * the min and max
                    */
                    for ( i = 0 ; i < menu->m.numarray.valN ; i++ )
                    {
                        if ( i == menu->m.numarray.cursor && menu->m.numarray.edit )
                        {
                            continue;
                        }
                        if ( menu->m.numarray.val[i] < menu->m.numarray.min[i] )
                        {
                            menu->m.numarray.val[i] = menu->m.numarray.min[i];
                        }
                        if ( menu->m.numarray.val[i] > menu->m.numarray.max[i] )
                        {
                            menu->m.numarray.val[i] = menu->m.numarray.max[i];
                        }
                    }
                    break;
                }

            default:
                return;
            }

            Pw_WindowRepaint(&uiMenuS.MenuWin);

            return;
        }

    case UI_MENU_TYPE_SCALE:
        {
            switch ( keycode )
            {
            case KEYSYM_CLEAR:
            case KEYSYM_SOFT_NEGATIVE:
                if ( menu->m.scale.doneCB )
                {
                    menu->m.scale.doneCB(menu->m.scale.doneCBarg,
                        menu->m.scale.curr);
                }
                uiMenuPop(1);
                return;

            case KEYSYM_RIGHT:
            case KEYSYM_UP:
            case KEYSYM_VOL_UP:
                {
                    if ( menu->m.scale.timeout )
                    {
                        pollerTaskStop(&menu->m.scale.timeoutTask);
                        pollerTaskTimeoutRel(&menu->m.scale.timeoutTask,
                            menu->m.scale.timeout/2);
                    }
                    if ( menu->m.scale.curr < menu->m.scale.max )
                    {
                        menu->m.scale.curr++;
                    }
                    else
                    {
                        return;
                    }
                    break;
                }

            case KEYSYM_LEFT:
            case KEYSYM_DOWN:
            case KEYSYM_VOL_DOWN:
                {
                    if ( menu->m.scale.timeout )
                    {
                        pollerTaskStop(&menu->m.scale.timeoutTask);
                        pollerTaskTimeoutRel(&menu->m.scale.timeoutTask,
                            menu->m.scale.timeout/2);
                    }
                    if ( menu->m.scale.min < menu->m.scale.curr )
                    {
                        menu->m.scale.curr--;
                    }
                    else
                    {
                        return;
                    }
                    break;
                }

            case KEYSYM_0:
            case KEYSYM_1:
            case KEYSYM_2:
            case KEYSYM_3:
            case KEYSYM_4:
            case KEYSYM_5:
            case KEYSYM_6:
            case KEYSYM_7:
            case KEYSYM_8:
            case KEYSYM_9:
                {
                    int num = keycode - KEYSYM_0;

                    if ( menu->m.scale.timeout )
                    {
                        pollerTaskStop(&menu->m.scale.timeoutTask);
                        pollerTaskTimeoutRel(&menu->m.scale.timeoutTask,
                            menu->m.scale.timeout/2);
                    }
                    if ( num < menu->m.scale.min )
                    {
                        num = menu->m.scale.min;
                    }
                    if ( num > menu->m.scale.max )
                    {
                        num = menu->m.scale.max;
                    }
                    menu->m.scale.curr = num;
                    break;
                }

            default:
                return;
            }

            if ( menu->m.scale.valueCB )
            {
                (*menu->m.scale.valueCB)(
                    menu->m.scale.curr,
                    menu->m.scale.min,
                    menu->m.scale.max);
            }
            if ( menu->m.scale.chgCB )
            {
                menu->m.scale.chgCB(menu->m.scale.chgCBarg, menu->m.scale.curr);
            }

            Pw_WindowRepaint(&uiMenuS.MenuWin);
            return;
        }

    case UI_MENU_TYPE_DIALOG:
        {

            switch ( keycode )
            {
            case KEYSYM_TALK:
                /* the talk button doesn't do anything in this menu */
                break;

            case KEYSYM_SOFT_POSITIVE:
            case KEYSYM_SELECT:
                /*
                * It is the callback's responsibility to remove
                * the dialog
                */
                if ( menu->m.dialog.selectCB )
                {
                    menu->m.dialog.selectCB(menu->m.dialog.selectCBarg);
                }
                return;

            case KEYSYM_CLEAR:
            case KEYSYM_SOFT_NEGATIVE:
                uiMenuPop(1);
                return;

            case KEYSYM_VOL_UP:
            case KEYSYM_VOL_DOWN:
                uiCallVolumeDialogStart();
                return;

            default:
                /*
                * Return false so the key is dispatch to the
                * previous menu, but still remove the dialog
                */
                break;
            }
            return;
        }

    case UI_MENU_TYPE_USER:
        if ( menu->m.user.eventHandler )
        {
            menu->m.user.eventHandler(menu, ev);
        }
        break;

    case UI_MENU_TYPE_SYMBOL_SELECTION:
        {
            switch ( keycode )
            {
            case KEYSYM_SOFT_POSITIVE:
                //
                // Select a symbol
                //
                if (menu->m.symbol_selection.doneCB)
                {
                    menu->m.symbol_selection.doneCB(menu->m.symbol_selection.doneCBarg,
                        menu->m.symbol_selection.str[menu->m.symbol_selection.cursorPos]);
                }
                break;
            case KEYSYM_SOFT_NEGATIVE:
                //
                // Back
                //
                s_ZComInputMethodState.ZComInputMethodTypeCurrent =
                    s_ZComInputMethodState.ZComInputMethodTypeLastInUsed;
                uiMenuPop(1);
                return;
            case KEYSYM_POUND:
                //
                // Toggle input method to lower case
                //
                s_ZComInputMethodState.ZComInputMethodTypeCurrent = 0;
                uiMenuPop(1);
                return;

            case KEYSYM_DOWN:
                {
                    //
                    // Move cursor right
                    //
                    menu->m.symbol_selection.cursorPos++;

                    if (menu->m.symbol_selection.cursorPos > strlen(menu->m.symbol_selection.str) - 1)
                    {
                        //
                        // Loop to begin
                        //
                        menu->m.symbol_selection.cursorPos = SYMBOL_SELECTION_LINE_NUMBER_SIZE;
                        break;
                    }

                    int iPosCol = menu->m.symbol_selection.cursorPos % SYMBOL_SELECTION_PER_LINE_SIZE;
                    if (0 == iPosCol)
                    {
                        //
                        // Cross line number
                        //
                        menu->m.symbol_selection.cursorPos += SYMBOL_SELECTION_LINE_NUMBER_SIZE;
                    }

                    break;
                }
            case KEYSYM_UP:
                {
                    //
                    // Move cursor left
                    //
                    menu->m.symbol_selection.cursorPos--;

                    if (menu->m.symbol_selection.cursorPos < SYMBOL_SELECTION_LINE_NUMBER_SIZE)
                    {
                        //
                        // Loop to end
                        //
                        menu->m.symbol_selection.cursorPos = strlen(menu->m.symbol_selection.str) - 1;
                        break;
                    }

                    int iPosCol = menu->m.symbol_selection.cursorPos % SYMBOL_SELECTION_PER_LINE_SIZE;
                    if ((SYMBOL_SELECTION_LINE_NUMBER_SIZE - 1) == iPosCol)
                    {
                        //
                        // Cross line number
                        //
                        menu->m.symbol_selection.cursorPos -= SYMBOL_SELECTION_LINE_NUMBER_SIZE;
                    }

                    break;
                }
            case KEYSYM_1:
            case KEYSYM_2:
            case KEYSYM_3:
            case KEYSYM_4:
            case KEYSYM_5:
            case KEYSYM_6:
            case KEYSYM_7:
            case KEYSYM_8:
            case KEYSYM_9:
                {
                    //
                    // move cursor to line 1~9, index 0~8
                    //
                    int iCursorLine = keycode - KEYSYM_1;
                    int iMaxline = (strlen(menu->m.symbol_selection.str) - 1) / SYMBOL_SELECTION_PER_LINE_SIZE;
                    int iPosCol = menu->m.symbol_selection.cursorPos % SYMBOL_SELECTION_PER_LINE_SIZE;

                    if (iCursorLine > iMaxline)
                    {
                        //
                        // Over max line
                        //
                        return;
                    }

                    menu->m.symbol_selection.cursorPos = iCursorLine * SYMBOL_SELECTION_PER_LINE_SIZE + iPosCol;

                    break;
                }
            case KEYSYM_0:
            case KEYSYM_STAR:
                //
                // Do nothing
                //
                return;
            }

            Pw_WindowRepaint(&uiMenuS.MenuWin);

            break;
        }

    case UI_MENU_TYPE_STATUS:
        {

            switch ( keycode )
            {
            case KEYSYM_TALK:
                /* the talk button doesn't do anything in this menu */
                break;

            case KEYSYM_END:
                /* pop the menu stack */
                uiMainWinKeyEnd();
                return;

            case KEYSYM_RIGHT:
            case KEYSYM_SOFT_POSITIVE:
            case KEYSYM_SELECT:
                if ( menu->m.status.failure )
                {
                    if ( menu->m.status.selectCB )
                    {
                        menu->m.status.selectCB(menu->m.status.selectCBarg);
                    }
                    else
                    {
                        uiMenuPop(1);
                    }
                }
                return;

            case KEYSYM_LEFT:
            case KEYSYM_CLEAR:
            case KEYSYM_SOFT_NEGATIVE:
                uiMenuPop(1);
                return;

            case KEYSYM_UP:
            case KEYSYM_VOL_UP:
            case KEYSYM_DOWN:
            case KEYSYM_VOL_DOWN:
                //uiPhoneSettingsEditVolumeStart();
                uiCallVolumeDialogStart();
                return;

            default:
                break;
            }
            return;
        }

    default:
        debugf("uiMenuWinEvents: Unknown menu type %d\n", menu->type);
        break;
    }
    return;
}

static void uiZeroKeyTimeoutTaskHandler(struct pollerTask *taskP)
{
    UIMenu *menu;

    if ( uiMenuS.MenuTop < 0 ) return;

    menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

    if ( 0
            || menu->type == UI_MENU_TYPE_NUM_ENTRY
            )
    {
        if ((menu->m.text.cursorPos > 0) &&
            (menu->m.text.str[menu->m.text.cursorPos - 1] == '0'))
        {
            menu->m.text.str[menu->m.text.cursorPos - 1] = '+';
        }
        menu->m.text.lastKeySym = KEYSYM_UNDEF;
        Pw_WindowRepaint(&uiMenuS.MenuWin);
    }
}

/*
 * Called after a timeout following each keypress.  This
 * is used in a text menu to determine when we switch to
 * the next character, rather than cycling the current
 * character.
 */
static void uiMenuTextKeySeqTimeoutTask(struct pollerTask *taskP)
{
    UIMenu *menu;

    if ( uiMenuS.MenuTop < 0 ) return;

    menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

    if ( 0
            || menu->type == UI_MENU_TYPE_TEXT_ENTRY
            || menu->type == UI_MENU_TYPE_NUM_ENTRY
            || menu->type == UI_MENU_TYPE_HEX_ENTRY
            || menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY//2007/04/09 lizhen
			|| menu->type == UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY //by mike
            || menu->type == UI_MENU_TYPE_TEXT_EX_ENTRY )
    {
        menu->m.text.lastKeySym = KEYSYM_UNDEF;
        Pw_WindowRepaint(&uiMenuS.MenuWin);
    }
}

/*
 * Called after a timeout following scale menu keypresses. If
 * function called, ends scale menu (updates value)
 */
static void uiMenuScaleTimeoutTask(struct pollerTask *taskP)
{
    #if 0       /* was */
    UIMenu *menu;

    if ( uiMenuS.MenuTop < 0 ) return;

    menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

    /* Call the done callback function */
    if ( menu->m.scale.doneCB ) {
        menu->m.scale.doneCB(menu->m.scale.doneCBarg,
                             menu->m.scale.curr);
    }
    #endif
    /* NOTE "menu" is invalid now */
    uiMenuPop(1);
}

/*
 * Called after a timeout following a dialog.
 * Pop the dialog.
 */
static void uiMenuDialogTimeoutTask(struct pollerTask *taskP)
{
    uiMenuPop(1);
}

void uiMenuTextEntryInit(const UIMenuTextEntryInit *init)
{
    UIMenu *menu = uiMenuPush(UI_MENU_TYPE_TEXT_ENTRY);

    menu->type               = UI_MENU_TYPE_TEXT_ENTRY;
    menu->title              = init->title;
    menu->m.text.prompt      = init->prompt;
    menu->m.text.doneCB      = init->doneCB;
    menu->m.text.doneCBarg   = init->doneCBarg;
    menu->m.text.shift       = init->shift;

    //2006/12/29    lizhen
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;


    sprcpy(menu->m.text.str, sizeof(menu->m.text.str), init->str);
    if ( init->bufSize ) {
        assert( init->bufSize <= sizeof(menu->m.text.str) );
        menu->m.text.bufSize = init->bufSize;
    } else {
        menu->m.text.bufSize = sizeof(menu->m.text.str);
    }
    menu->m.text.cursorPos   = init->cursorPos;
    menu->m.text.lastKeySym  = KEYSYM_UNDEF;

    menu->m.text.bZComInputMethodTypeInit   = init->bZComInputMethodTypeInit;
    menu->m.text.ZComInputMethodTypeIndex   = init->ZComInputMethodTypeIndex;

    if (menu->m.text.bZComInputMethodTypeInit == false)
    {
        menu->m.text.bZComInputMethodTypeInit = true;
        menu->m.text.ZComInputMethodTypeIndex = 0;//{ 1, langPage_eLowerCase },
    }

    if ( !menu->m.text.str[0] )
    {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
    }
    else
    {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Clear);
    }
    uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Done);
    mainthreadTaskInit(&menu->m.text.keyTimeoutTask,
            "uiKeySeqTask", uiMenuTextKeySeqTimeoutTask);
    pollerTaskPrioritySet(&menu->m.text.keyTimeoutTask,
            CONFIG_PRIORITY_UI );

    uiMenuActivate();
}

//
//by Mike
//
static void uiMenuPKeyTextKeySeqTimeoutTask(struct pollerTask *taskP)
{
    UIMenu *menu;

    if ( uiMenuS.MenuTop < 0 ) return;

    menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

    if ( menu->type == UI_MENU_TYPE_PKEY_TEXT_ENTRY )
    {
		//Need more check?
		//How to change the title text?
        menu->m.text.lastKeySym = KEYSYM_UNDEF;
        Pw_WindowRepaint(&uiMenuS.MenuWin);
    }

}

void uiMenuPKeyTextEntryInit(const UIMenuPKeyTextEntryInit *init)
{
    UIMenu *menu = uiMenuPush(UI_MENU_TYPE_PKEY_TEXT_ENTRY);

    menu->type               = UI_MENU_TYPE_PKEY_TEXT_ENTRY;
    menu->title              = init->title;
    menu->m.text.prompt      = init->prompt;
    menu->m.text.doneCB      = init->pkeyCB;
    menu->m.text.doneCBarg   = init->pkeyCBarg;
    menu->m.text.shift       = init->shift;

	menu->m.text.pkeyTextNilEntryIdx   = init->pkeyTextNilEntryIdx;
    menu->m.text.pkeyTextNotNilEntryIdx = init->pkeyTextNotNilEntryIdx;

    //2006/12/29    lizhen
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;

    sprcpy(menu->m.text.str, sizeof(menu->m.text.str), init->str);
    if ( init->bufSize ) {
        assert( init->bufSize <= sizeof(menu->m.text.str) );
        menu->m.text.bufSize = init->bufSize;
    } else {
        menu->m.text.bufSize = sizeof(menu->m.text.str);
    }
    menu->m.text.cursorPos   = init->cursorPos;
    menu->m.text.lastKeySym  = KEYSYM_UNDEF;

    if ( !menu->m.text.str[0] )
    {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
		uiMenuSoftKeyPositive(menu->softKey, init->pkeyTextNilEntryIdx);
    }
    else
    {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Clear);
		uiMenuSoftKeyPositive(menu->softKey, init->pkeyTextNotNilEntryIdx);
    }

	//task for key event
    mainthreadTaskInit(&menu->m.text.keyTimeoutTask,
            "uiPKeySeqTask", uiMenuPKeyTextKeySeqTimeoutTask);
    pollerTaskPrioritySet(&menu->m.text.keyTimeoutTask,
            CONFIG_PRIORITY_UI );

    uiMenuActivate();
}

//by Mike
void uiMenuText_List_PBook_V2_EntryInit(const UIMenuText_List_PBook_V2_EntryInit *init)
{
	UIMenu *menu = uiMenuPush(UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY);

    menu->type               = UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY;
    menu->title              = init->title;
    menu->m.text.prompt      = init->prompt;
    menu->m.text.doneCB      = init->doneCB;
    menu->m.text.doneCBarg   = init->doneCBarg;
    menu->m.text.shift       = init->shift;

	//use pkeyTextNilEntryIdx as the entry
	//by Mike
	menu->m.text.pkeyTextNilEntryIdx   = init->pkeyTextEntryIdx;
	menu->m.text.showPBookP2P = init->showPBookP2P;

    //2006/12/29    lizhen
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;

    sprcpy(menu->m.text.str, sizeof(menu->m.text.str), init->str);
    if ( init->bufSize ) {
        assert( init->bufSize <= sizeof(menu->m.text.str) );
        menu->m.text.bufSize = init->bufSize;
    } else {
        menu->m.text.bufSize = sizeof(menu->m.text.str);
    }
    menu->m.text.cursorPos   = init->cursorPos;
    menu->m.text.lastKeySym  = KEYSYM_UNDEF;

    if ( !menu->m.text.str[0] )
    {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
    }
    else
    {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Clear);
    }
	uiMenuSoftKeyPositive(menu->softKey, init->pkeyTextEntryIdx);

	//task for key event
    mainthreadTaskInit(&menu->m.text.keyTimeoutTask,
            "uiPKeySeqTask", uiMenuTextKeySeqTimeoutTask); //same as old
    pollerTaskPrioritySet(&menu->m.text.keyTimeoutTask,
            CONFIG_PRIORITY_UI );

    uiMenuActivate();
}

//2007/04/09    lizhen
void uiMenuTextExEntryInit(const UIMenuTextEntryInit *init)
{
    UIMenu *menu = uiMenuPush(UI_MENU_TYPE_TEXT_EX_ENTRY);

    menu->type               = UI_MENU_TYPE_TEXT_EX_ENTRY;
    menu->title              = init->title;
    menu->m.text.prompt      = init->prompt;
    menu->m.text.doneCB      = init->doneCB;
    menu->m.text.doneCBarg   = init->doneCBarg;
    menu->m.text.shift       = init->shift;

    //2006/12/29    lizhen
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;
    menu->m.text.nodeCBArgEx = init->nodeCBArgEx;

    // junky 2007-4-24
    menu->m.text.nPosText    = 0;

    menu->m.text.nScrollWaitCount = 0;

    sprcpy(menu->m.text.str, sizeof(menu->m.text.str), init->str);
    if ( init->bufSize ) {
        assert( init->bufSize <= sizeof(menu->m.text.str) );
        menu->m.text.bufSize = init->bufSize;
    } else {
        menu->m.text.bufSize = sizeof(menu->m.text.str);
    }
    menu->m.text.cursorPos   = init->cursorPos;
    menu->m.text.lastKeySym  = KEYSYM_UNDEF;

    if ( !menu->m.text.str[0] ) {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
    } else {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Clear);
    }
    uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Options);
    mainthreadTaskInit(&menu->m.text.keyTimeoutTask,
            "uiKeySeqTask", uiMenuTextKeySeqTimeoutTask);
    pollerTaskPrioritySet(&menu->m.text.keyTimeoutTask,
            CONFIG_PRIORITY_UI );

    uiMenuActivate();
}

void uiMenuNumEntryInit(const UIMenuNumEntryInit *init)
{
    UIMenu *menu = uiMenuPush(UI_MENU_TYPE_NUM_ENTRY);
    LangIdx doneKey;

    menu->type               = UI_MENU_TYPE_NUM_ENTRY;
    menu->title              = init->title;
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;
    menu->m.text.prompt      = init->prompt;
    menu->m.text.doneCB      = init->doneCB;
    menu->m.text.doneCBarg   = init->doneCBarg;
    menu->m.text.talkDone    = init->talkDone;
    sprcpy(menu->m.text.str, sizeof(menu->m.text.str), init->str);
    if ( init->bufSize ) {
        assert( init->bufSize <= sizeof(menu->m.text.str) );
        menu->m.text.bufSize = init->bufSize;
    } else {
        menu->m.text.bufSize = sizeof(menu->m.text.str);
    }
    menu->m.text.cursorPos   = init->cursorPos;
    menu->m.text.lastKeySym  = KEYSYM_UNDEF;

    doneKey = menu->m.text.talkDone ? LANG_SoftKey_Talk : LANG_SoftKey_Done;

    if ((g_zcomKeypadLockKeyCombination.next_keycode == KEYSYM_SOFT_POSITIVE) &&
        (zcom_uiKeyPadLockS.zcom_KeyPadLockState == ZCOM_KEYPAD_UNLOCK_WAIT_NEXT_KEY))
    {
        doneKey = LANG_ZCOM_STRING_RES_SoftKey_Text_KeyPad_Lock;
    }

    if ( !menu->m.text.str[0] ) {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
    } else {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Clear);
    }
    uiMenuSoftKeyPositive(menu->softKey, doneKey);

    mainthreadTaskInit(&menu->m.text.keyTimeoutTask,
            "uiKeySeqTask", uiMenuTextKeySeqTimeoutTask);
    pollerTaskPrioritySet(&menu->m.text.keyTimeoutTask,
            CONFIG_PRIORITY_UI );

    mainthreadTaskInit(&menu->m.text.zeroKeyTimeoutTask,
            "uiZeroKeyTimeoutTask", uiZeroKeyTimeoutTaskHandler);
    pollerTaskPrioritySet(&menu->m.text.zeroKeyTimeoutTask,
            CONFIG_PRIORITY_UI );

    if (init->preInputKeySym == KEYSYM_0)
    {
        pollerTaskTimeoutRel(
            &menu->m.text.zeroKeyTimeoutTask,
            pollerClockMsecToTicks(CFG_MENU_TEXT_ENTRY_SCROLL_TIMEOUT_MS)
            );
    }

    uiMenuActivate();
}

void uiMenuHexEntryInit(const UIMenuNumEntryInit *init)
{
    UIMenu *menu = uiMenuPush(UI_MENU_TYPE_HEX_ENTRY);
    LangIdx doneKey;

    menu->type               = UI_MENU_TYPE_HEX_ENTRY;
    menu->title              = init->title;
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;
    menu->m.text.prompt      = init->prompt;
    menu->m.text.doneCB      = init->doneCB;
    menu->m.text.doneCBarg   = init->doneCBarg;
    menu->m.text.talkDone    = init->talkDone;
    sprcpy(menu->m.text.str, sizeof(menu->m.text.str), init->str);
    if ( init->bufSize ) {
        assert( init->bufSize <= sizeof(menu->m.text.str) );
        menu->m.text.bufSize = init->bufSize;
    } else {
        menu->m.text.bufSize = sizeof(menu->m.text.str);
    }
    menu->m.text.cursorPos   = init->cursorPos;
    menu->m.text.lastKeySym  = KEYSYM_UNDEF;

    doneKey = menu->m.text.talkDone ? LANG_SoftKey_Talk : LANG_SoftKey_Done;

    if ( !menu->m.text.str[0] ) {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
    } else {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Clear);
    }
    uiMenuSoftKeyPositive(menu->softKey, doneKey);
    mainthreadTaskInit(&menu->m.text.keyTimeoutTask,
            "uiKeySeqTask", uiMenuTextKeySeqTimeoutTask);
    pollerTaskPrioritySet(&menu->m.text.keyTimeoutTask,
            CONFIG_PRIORITY_UI );

    uiMenuActivate();
}

void uiMenuNumArrayEntryInit(const UIMenuNumArrayEntryInit *init)
{
    UIMenu *menu = uiMenuPush(UI_MENU_TYPE_NUMARRAY_ENTRY);
    int i = 0;

    menu->type                 = UI_MENU_TYPE_NUMARRAY_ENTRY;
    menu->title                = init->title;
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;
    menu->m.numarray.prompt    = init->prompt;
    menu->m.numarray.doneCB    = init->doneCB;
    menu->m.numarray.doneCBarg = init->doneCBarg;

    menu->m.numarray.valN    = init->valN;
    menu->m.numarray.bIgnoreBackEvent    = init->bIgnoreBackEvent;

    for ( i = 0 ; i < init->valN ; i++ ) {
        menu->m.numarray.val[i]   = init->val[i];
        menu->m.numarray.min[i]   = init->min[i];
        menu->m.numarray.max[i]   = init->max[i];
        menu->m.numarray.leadZ[i] = init->leadZ[i];
        menu->m.numarray.sep[i]   = init->sep[i];
    }

    if (!init->bIgnoreBackEvent)
    {
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
    }
    uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Set);
    uiMenuActivate();
}

void uiMenuScaleInit(const UIMenuScaleInit *init)
{
    UIMenu *menu = uiMenuPush(UI_MENU_TYPE_SCALE);

    menu->type                 = UI_MENU_TYPE_SCALE;
    menu->popWithParent        = 1;
    menu->title                = init->title;
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;
    menu->m.scale.prompt       = init->prompt;
    menu->m.scale.doneCB       = init->doneCB;
    menu->m.scale.doneCBarg    = init->doneCBarg;
    menu->m.scale.chgCB        = init->chgCB;
    menu->m.scale.chgCBarg     = init->chgCBarg;
    menu->m.scale.valueCB      = init->valueCB;
    menu->m.scale.showValue    = init->showValue;
    menu->m.scale.min          = init->min;
    menu->m.scale.max          = init->max;
    menu->m.scale.curr         = init->curr;
    menu->m.scale.timeout      = init->timeout;

    mainthreadTaskInit(&menu->m.scale.timeoutTask, "uiScaleTimeout",
                       uiMenuScaleTimeoutTask);
    pollerTaskPrioritySet(&menu->m.scale.timeoutTask,
            CONFIG_PRIORITY_UI );

    if ( init->timeout ) {
        pollerTaskTimeoutRel(&menu->m.scale.timeoutTask, init->timeout);
    }

    if ( menu->m.scale.curr < init->min ) {
        menu->m.scale.curr = init->min;
    }
    if ( menu->m.scale.curr > init->max ) {
        menu->m.scale.curr = init->max;
    }

    /* Do not allow soft keys for windows that timeout because
     * otherwise there would be nasty race conditions.
     */
    if ( ! init->timeout ) {
        /* NOTE! All sliders should have immediate effect */
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
    }
    uiMenuActivate();
}

UIMenu * uiMenuDialogInit(const UIMenuDialogInit *init, UIMenu *oldmenu )
{
    UIMenu *menu;
    if ( oldmenu ) {
        menu = oldmenu;
        uiMenuClean(menu);
    } else {
        menu = uiMenuPush(UI_MENU_TYPE_DIALOG);
    }

    menu->type                 = UI_MENU_TYPE_DIALOG;
    menu->title                = init->title;
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;
    menu->m.dialog.selectCB    = init->selectCB;
    menu->m.dialog.selectCBarg = init->selectCBarg;
    sprcpy(menu->m.dialog.str, sizeof(menu->m.dialog.str), init->str);
    menu->m.dialog.prompt      = init->prompt;
    menu->m.dialog.protect     = init->protect;
    menu->m.dialog.isUsedForPing = init->isUsedForPing;

    mainthreadTaskInit(&menu->m.dialog.timeoutTask, "uiDialogTimeout",
                       uiMenuDialogTimeoutTask);
    pollerTaskPrioritySet(&menu->m.dialog.timeoutTask,
            CONFIG_PRIORITY_UI );

    if ( init->timeout ) {
        menu->m.dialog.timeout = init->timeout;
        pollerTaskTimeoutRel(&menu->m.dialog.timeoutTask, init->timeout);
    }

    if ( init->customSoftKey ) {
        for ( int i = 0; i < KEYPAD_SOFTKEY_NUM; i++ ) {
            menu->softKey[i] = init->softKey[i];
        }
    } else if ( menu->m.dialog.selectCB ) {
        /*
         * Install default softkeys only if there is a callback
         */
        uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Cancel);
        uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Yes);
    }

    /*
     * For dialog message (some of which are asynchoronous)
     * turn on the backlight.
     */
    dispBacklightOn();

    /*
     * Activate the menu if it is on top.
     */
    if ( menu == &uiMenuS.MenuStack[uiMenuS.MenuTop] ) {
        uiMenuActivate();
    }
    return menu;
}

/*
 * Display a message which is auto removed after a short time
 */
void uiMenuDialogMesgTimed(
        LangIdx title,                 /* title of dialog */
        const char *str                /* string to display */
    )
{
    UIMenuDialogInit init;
    memset(&init, 0, sizeof(init));
    init.title.textIdx = title;
    sprcpy(init.str, sizeof(init.str), str);
    init.timeout = pollerClockMsecToTicks(CFG_MENU_DIALOG_TIMEOUT_MS);

    uiMenuDialogInit(&init, NULL);
}


/*
 * Display a message for a particular error. Removed by user.
 */
void uiMenuDialogErrMsg(
        LangIdx err
    )
{
    UIMenuDialogInit init;
    memset(&init, 0, sizeof(init));
    init.title.textIdx = LANG_err_msg_title;
    sprcpy(init.str, sizeof(init.str), langGet(err));
    uiMenuDialogInit(&init, NULL);
}


/*
 * Display a message with a Yes prompt.
 */
UIMenu * uiMenuDialogMesgYes(
        LangIdx title,
        const char *str,
        UICBSelect *selectCB,          /* callback when selected */
        void *selectCBarg,             /* argument to select callback */
        void (*cleanupCB)(void *),     /* NULL or called when menu popped */
        void *cleanupCBArg,            /* passed to cleanupCB */
        UIMenu *oldmenu
    )
{
    UIMenuDialogInit init;

    memset(&init, 0, sizeof(init));

    init.title.textIdx           = title;
    sprcpy(init.str, sizeof(init.str), str);
    init.selectCB                = selectCB;
    init.selectCBarg             = selectCBarg;
    init.cleanupCB               = cleanupCB;
    init.cleanupCBArg            = cleanupCBArg;

    return uiMenuDialogInit(&init, oldmenu);
}


//2006/11/18    lizhen
UIMenu * uiMenuDialogMesgOk(
        UIMenuText title,
        const char *str
        )
{
    UIMenuDialogInit init;
    memset(&init, 0, sizeof(init));

    init.title = title;
    sprcpy(init.str, sizeof(init.str), str);
    init.selectCB                = NULL;
    init.selectCBarg             = NULL;
    init.cleanupCB               = NULL;
    init.cleanupCBArg            = NULL;

    init.customSoftKey = 1;
    uiMenuSoftKeyNegative(init.softKey, LANG_SoftKey_Back);

    return uiMenuDialogInit(&init, NULL);

}

//void uiMenuCalendarInit(const UIMenuCalendarInit *init)
//{
//    UIMenu *menu = uiMenuPush(UI_MENU_TYPE_CALENDAR);
//
//    menu->type                    = UI_MENU_TYPE_CALENDAR;
//    menu->title                   = init->title;
//    menu->cleanupCB            = init->cleanupCB;
//    menu->cleanupCBArg         = init->cleanupCBArg;
//    menu->m.calendar.selectCB     = init->selectCB;
//    menu->m.calendar.selectCBarg  = init->selectCBarg;
//    menu->m.calendar.sec          = init->sec;
//    menu->m.calendar.textCBargSec = init->textCBargSec;
//    menu->m.calendar.firstDig     = -1;
//    if ( menu->m.calendar.textCBargSec ) {
//        menu->title.textCBarg = (void*)menu->m.calendar.sec;
//    }
//
//    uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);
//    if ( menu->m.calendar.selectCB )
//        uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Select);
//    uiMenuActivate();


UIMenu * uiMenuUserInit(const UIMenuUserInit *init, UIMenu *oldmenu)
{
    UIMenu *menu;
    if ( oldmenu ) {
        menu = oldmenu;
        uiMenuClean(menu);
    } else {
        menu = uiMenuPush(UI_MENU_TYPE_USER);
    }

    menu->type                   = UI_MENU_TYPE_USER;
    menu->title                  = init->title;
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;
    /* NOTE! softKey[] arrays are layed out from left to right always! */
    for ( int i = 0 ; i < KEYPAD_SOFTKEY_NUM ; i++ ) {
        menu->softKey[i] = init->softKey[i];
    }
    menu->m.user.eventHandler    = init->eventHandler;
    menu->m.user.repaintHandler  = init->repaintHandler;
    menu->m.user.softKeyUpdate   = init->softKeyUpdate;
    menu->m.user.userData        = init->userData;
    menu->currNode               = init->currNode;

#if CFG_HTTPD_ENABLE
    menu->m.user.httpNavGoTo     = init->httpNavGoTo;
    menu->m.user.httpNavLink     = init->httpNavLink;
    menu->m.user.httpContent     = init->httpContent;
    menu->m.user.httpSubmit      = init->httpSubmit;
#endif

    /*
     * Activate the menu if it is on top.
     */
    if ( menu == &uiMenuS.MenuStack[uiMenuS.MenuTop] ) {
        uiMenuActivate();
    }
    return menu;
}

static void uiMenuSymbolSelectionDone(void *arg, const char symbol)
{
    UIMenu* TextEntry = (UIMenu*)arg;
    char utf8[UNICODE_CHAR_BYTES_MAX] = {0};
    UnicodeChar uniChar = ((symbol == 'S')?' ':symbol);
    int utf8Sz = 0;
    utf8Sz = unicodeChar2Utf8(utf8, sizeof(utf8), uniChar);

    if (strlen(TextEntry->m.text.str) + utf8Sz >= TextEntry->m.text.bufSize)
    {
        //
        // Text entry can not accept any more
        //
    }
    else
    {
        for (int i = strlen(TextEntry->m.text.str) ; i >= TextEntry->m.text.cursorPos ; i-- )
        {
            TextEntry->m.text.str[i + utf8Sz] = TextEntry->m.text.str[i];
        }
        memcpy(&TextEntry->m.text.str[TextEntry->m.text.cursorPos], utf8, utf8Sz);
        TextEntry->m.text.cursorPos += utf8Sz;
        TextEntry->m.text.lastUniChar = uniChar;
        TextEntry->m.text.lastUtf8Sz  = utf8Sz;
    }

//     s_ZComInputMethodState.ZComInputMethodTypeCurrent =
//                        s_ZComInputMethodState.ZComInputMethodTypeLastInUsed;
    uiMenuPop(1);
}

static void uiMenuSymbolSelectionStart(void)
{
    UIMenuSymbolSelectionInit init;

    memset(&init, 0, sizeof(init));

    init.title.textIdx = LANG_Symbol_Selction_title;
    init.doneCB = uiMenuSymbolSelectionDone;
    sprcpy(init.str, sizeof(init.str), SYMBOL_SELECTION_CONTENT);
    init.cursorPos = 2;

    uiMenuSymbolSelectionInit(&init);
}

void uiMenuSymbolSelectionInit(const UIMenuSymbolSelectionInit *init)
{
    UIMenu* TextEntry = &uiMenuS.MenuStack[uiMenuS.MenuTop];
    if (NULL == TextEntry || (TextEntry->type != UI_MENU_TYPE_TEXT_ENTRY
         && TextEntry->type != UI_MENU_TYPE_TEXT_EX_ENTRY
		 && TextEntry->type != UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY //by Mike
		 && TextEntry->type != UI_MENU_TYPE_PKEY_TEXT_ENTRY //by Mike
		 ))//2007/04/09 lizhen
    {
        return;
    }

    UIMenu *menu = uiMenuPush(UI_MENU_TYPE_SYMBOL_SELECTION);

    menu->type = UI_MENU_TYPE_SYMBOL_SELECTION;
    menu->title = init->title;
    sprcpy(menu->m.symbol_selection.str, sizeof(menu->m.symbol_selection.str), init->str);
    menu->m.symbol_selection.cursorPos = init->cursorPos;
    menu->cleanupCB = init->cleanupCB;
    menu->cleanupCBArg = init->cleanupCBArg;
    menu->m.symbol_selection.doneCB = init->doneCB;
    menu->m.symbol_selection.doneCBarg = (void*)TextEntry;   // Text entry handle

    uiMenuSoftKeyPositive(menu->softKey, LANG_SoftKey_Select);
    uiMenuSoftKeyNegative(menu->softKey, LANG_SoftKey_Back);

    uiMenuActivate();
}

UIMenu * uiMenuStatusInit(const UIMenuStatusInit *init)
{
    UIMenu *menu;
    menu = uiMenuPush(UI_MENU_TYPE_STATUS);

    menu->type                 = UI_MENU_TYPE_STATUS;
    menu->title                = init->title;
    menu->cleanupCB            = init->cleanupCB;
    menu->cleanupCBArg         = init->cleanupCBArg;
    menu->m.status.selectCB    = init->selectCB;
    menu->m.status.selectCBarg = init->selectCBarg;
    menu->m.status.updateCB    = init->updateCB;
    menu->refreshTimeoutSeconds = init->updateSeconds;

#if CFG_HTTPD_ENABLE
    menu->m.status.httpNavGoTo     = init->httpNavGoTo;
    menu->m.status.httpNavLink     = init->httpNavLink;
    menu->m.status.httpContent     = init->httpContent;
    menu->m.status.httpSubmit      = init->httpSubmit;
#endif

    /*
     * Activate the menu if it is on top.
     */
    if ( menu == &uiMenuS.MenuStack[uiMenuS.MenuTop] ) {
        uiMenuActivate();
    }
    return menu;
}
/*===========================================================================*/
/*============== Http Interface =============================================*/
/*===========================================================================*/

#if CFG_HTTPD_ENABLE

/*
 * Navigate to the given URL
 */
void uiMenuHttpNavGoTo(char *url)
{
    UIMenu *menu;
    int    node, i;
//  debugf("uiMenuHttpNavGoTo--->\n");
    if ( *url == '/' ) url++;
    for ( i = 0 ; i < UI_MENU_MAXDEPTH && url && *url ; i++ )
    {
        if ( i > uiMenuS.MenuTop ) break;

        menu = &uiMenuS.MenuStack[i];

        if ( url[0] < '0' || url[0] > '9' )
        {
            node = -1;
        }
        else
        {
            node = atoi(url);
        }
        url = strchr(url, '/');
        if ( url && *url == '/' ) url++;
        if ( node < 0 || !url || !*url ) break;

        switch ( menu->type ) {

            case UI_MENU_TYPE_LIST:
                if ( menu->m.list.nodeCntCB )
                {
                    menu->m.list.nodeCntCB(&menu->currNode,
                                           &menu->m.list.nNodes,
                                           menu->m.list.nodeCBArg);
                }
                if ( menu->currNode == node && i < uiMenuS.MenuTop ) continue;

                /*
                 * Wrong menu selected - pop everything above and select
                 * the new menu node.
                 */
                while ( i < uiMenuS.MenuTop ) uiMenuPop(0);
                if ( node < menu->m.list.nNodes ) {
                    menu->currNode = node;
                    if ( menu->m.list.nodeSelectCB ) {
                        menu->m.list.nodeSelectCB(node,
                                                  menu->m.list.nodeCBArg);
                    } else if ( menu->m.list.nodes[node].selectCB ) {
                        menu->m.list.nodes[node].selectCB(
                                    menu->m.list.nodes[node].selectCBarg);
                    }
                }
                break;

            case UI_MENU_TYPE_USER:
                if ( menu->m.user.httpNavGoTo ) {
                    menu->currNode = node;
                    menu->m.user.httpNavGoTo(node, i, uiMenuS.MenuTop, 1);
                }
                break;

            case UI_MENU_TYPE_STATUS:
                if ( menu->m.status.httpNavGoTo ) {
                    menu->currNode = node;
                    menu->m.status.httpNavGoTo(node, i, uiMenuS.MenuTop, 1);
                }
                break;

            default:
                break;
        }
    }

    /*
     * Pop any extra menus if necessary
     */
    if ( i < uiMenuS.MenuTop ) {
        uiMenuPopLevel(i, 1);
    }

#if 0
    while ( i < uiMenuS.MenuTop ) {
        menu = &uiMenuS.MenuStack[i];
        if ( menu->type != UI_MENU_TYPE_LIST ) {
            i++;
        } else {
            uiMenuPop(0);
        }
    }
#endif
}

/*
 * Display navigation links given the current menu stack
 */
void uiMenuHttpNavLink(HttpdOutput *output, char *parentUrl, int level)
{
    char   str[UI_MENU_MAXSTR];
    char   url[UI_MENU_MAXSTR];
    UIMenu *menu;

//  debugf("uiMenuHttpNavLink--->parentUrl=%s level=%d\n", parentUrl, level);
    if ( level > uiMenuS.MenuTop ) return;
    menu = &uiMenuS.MenuStack[level];
    switch ( menu->type ) {

        case UI_MENU_TYPE_LIST:
            if ( menu->m.list.valueList ) {
                /*
                 * This is a terminal menu with a list of values - these
                 * are not navigation links to other menus, so don't
                 * display them.
                 */
                break;
            }
            if ( menu->m.list.nodeCntCB ) {
                menu->m.list.nodeCntCB(&menu->currNode,
                                       &menu->m.list.nNodes,
                                       menu->m.list.nodeCBArg);
            }
            for ( int i = 0 ; i < menu->m.list.nNodes ; i++ )
            {
                if ( menu->m.list.nodeTextCB )
                {
                    menu->m.list.nodeTextCB(str, sizeof(str), i,
                                            menu->m.list.nodeCBArg);
                }
                else
                {
                    uiMenuTextGet(&menu->m.list.nodes[i].text, str, sizeof(str));
                }


                if (strcmp(str, langGet(LANG_Zcom_Menu_Call_Log)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Menu_Information_title)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Menu_Ping_Test)) == 0
                    || strcmp(str, langGet(LANG_Menu_Setup_datetime)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Menu_PhoneSetting_FW_Upgrade)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Menu_Reconnect)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Menu_Site_Scan)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Profile_Add_New)) == 0
                    || strcmp(str, langGet(LANG_Menu_Setup_wlan_autoconfig)) == 0)
                {
                    continue;
                }

                spr(url, sizeof(url), "%s/%d", parentUrl, i);
                if ( menu->currNode == i && level < uiMenuS.MenuTop )
                {

                    uiHttpNavLink(output, str, url, 1/*selected*/, level);
                    uiMenuHttpNavLink(output, url, level + 1);
                }
                else
                {
                    uiHttpNavLink(output, str, url, 0/*!selected*/, level);
                }
            }
            break;

        case UI_MENU_TYPE_USER:
            if ( menu->m.user.httpNavLink ) {
                menu->m.user.httpNavLink(menu, output, parentUrl, level);
            }
            break;

        case UI_MENU_TYPE_STATUS:
            if ( menu->m.status.httpNavLink ) {
                menu->m.status.httpNavLink(menu, output, parentUrl, level);
            }
            break;

        default:
            break;
    }
}

/*
 * Display the content for the current top-level menu
 */
void uiMenuHttpContent(HttpdOutput *output)
{
    UIMenu *menu;
    char   str[UI_MENU_MAXSTR];
    int    i;
    char   url[UI_MENU_MAXSTR];
    int level;

//  debugf("uiMenuHttpContent--->\n");
    spr(url, sizeof(url), "/menu");
    for ( level = 0; level < uiMenuS.MenuTop; level++ )
    {
        menu = &uiMenuS.MenuStack[level];
        aspr(url, sizeof(url), "/%d", menu->currNode );
    }
    menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

    if ( menu->title.textIdx != LANG_NULL || menu->title.textCB )
    {
        uiMenuTextGet(&menu->title, str, sizeof(str));
    }
    else
    {
        sprcpy(str, sizeof(str), "Menu");       /* TODO: use langGet */
    }

    httpdOutPuts(output,"<BODY> <blockquote><p>&nbsp;</p> <DIV id=Content>");


    httpdOutPuts(output,
            "<form action=\"/submit\" method=\"get\" name=\"form1\">\n");

    /* Put the soft keys at the top: */
    httpdOutPuts(output,
                 "<input type=\"hidden\" name=\"soft\" value=\"\">");

    httpdOutPuts(output, "<div align= \"right\">");

    httpdOutPuts(output, "</div>");

    /* NOTE! softKey[] arrays are layed out from left to right always! */
    for ( i = 0 ; i < KEYPAD_SOFTKEY_NUM ; i++ )
    {
        if ( menu->softKey[i].text.textIdx == 0
                && !menu->softKey[i].text.textCB )
        {
            /*
             * Zero text index and CB means soft key is disabled.
             */
            continue;
        }
        uiMenuTextGet(&menu->softKey[i].text, str, sizeof(str));
        if (strcmp(str, "Clear") == 0)
        {
            continue;
        }
        httpdOutSpr(output,
                    "<input type=\"button\" value=\"%s\" name=\"soft%d\""
                    " onClick=\"document.form1.soft.value = %d; "
                    " document.form1.submit();\">",
                    str, i, i);
    }
    httpdOutPuts(output, "<br>\n");

    /* Below that, put the menu list, user list, or other output */
//debugf("uiMenuHttpContent menu->type=====%d\n", menu->type);

    switch ( menu->type ) {
        case UI_MENU_TYPE_LIST: {
            if ( menu->m.list.nodeCntCB )
            {
                menu->m.list.nodeCntCB(&menu->currNode,
                                       &menu->m.list.nNodes,
                                       menu->m.list.nodeCBArg);
            }
            for ( i = 0 ; i < menu->m.list.nNodes ; i++ )
            {
                if ( menu->m.list.nodeTextCB )
                {
                    menu->m.list.nodeTextCB(str, sizeof(str), i,
                                            menu->m.list.nodeCBArg);
                }
                else
                {
                    uiMenuTextGet(&menu->m.list.nodes[i].text, str, sizeof(str));
                }
                if (strcmp(str, langGet(LANG_Zcom_Menu_Call_Log)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Menu_Information_title)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Menu_Ping_Test)) == 0
                    || strcmp(str, langGet(LANG_Menu_Setup_datetime)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Menu_PhoneSetting_FW_Upgrade)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Menu_Reconnect)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Menu_Site_Scan)) == 0
                    || strcmp(str, langGet(LANG_Zcom_Profile_Add_New)) == 0
                    || strcmp(str, langGet(LANG_Menu_Setup_wlan_autoconfig)) == 0)
                {
                    continue;
                }

                if ( menu->m.list.configList && menu->m.list.configNode == i)
                {
                    memmove(str+1, str, sizeof(str)-3);
                    str[sizeof(str)-1] = 0;
                    str[0] = '*';       /* mark as configured */
                    //str[1] = '*';       /* mark as configured */
                }
                httpdOutPuts(output, "<br>\n");
                httpdOutSpr(output, "<input type=\"radio\" name=\"v\""
                                "value=\"%d\"%s>\n",
                                i,
                                i == menu->currNode ? " checked" : "");
                #if 0   // was
                httpdOutSpr(output,
                                "<a href=\"/submit?v=%d&select=1\">%s</a>\n",
                                i, str);
                #else
                httpdOutSpr(output,
                                "<a href=\"%s/%d/menu.html\">%s</a>\n",
                                url, i,
                                str);
                #endif
                httpdOutPuts(output, "</li>\n");
            }
            break;
        }

        case UI_MENU_TYPE_NUM_ENTRY:
        case UI_MENU_TYPE_HEX_ENTRY:
        case UI_MENU_TYPE_TEXT_EX_ENTRY://2007/04/09    lizhen
        case UI_MENU_TYPE_TEXT_ENTRY:
		case UI_MENU_TYPE_PKEY_TEXT_ENTRY: //by Mike
		case UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY: //by Mike
			{
            /*
             * Render the prompt
             */
            uiMenuTextGet(&menu->m.text.prompt, str, sizeof(str));
            if (strcmp(str, "Search Name") == 0)
            {
                break;
            }
            httpdOutSpr(output, "%s ", str);
            /*
             * TODO: URL escaping; checking characters with javascript...
             */
            httpdOutSpr(output, "<input type=\"text\" name=\"v\" size=\"%u\""
                            "maxlength=\"%u\" value=\"%s\">\n",
                            (unsigned)sizeof(menu->m.text.str),
                            (unsigned)sizeof(menu->m.text.str),
                            menu->m.text.str);
            break;
        }

        case UI_MENU_TYPE_NUMARRAY_ENTRY: {
            /*
             * Render the prompt
             */
            uiMenuTextGet(&menu->m.numarray.prompt, str, sizeof(str));
            httpdOutSpr(output, "%s ", str);

            for ( i = 0 ; i < menu->m.numarray.valN ; i++ ) {
                char fmt[20], str[UI_MENU_MAXSTR];
                int len;

                if ( menu->m.numarray.leadZ[i] ) {
                    spr(fmt, sizeof(fmt), "%%0%dd", menu->m.numarray.leadZ[i]);
                    len = menu->m.numarray.leadZ[i];
                } else {
                    sprcpy(fmt, sizeof(fmt), "%d");
                    /*
                     * Crude way to get text box length
                     */
                    spr(str, sizeof(str), "%d", menu->m.numarray.max[i]);
                    len = strlen(str);
                }
                spr(str, sizeof(str), fmt, menu->m.numarray.val[i]);
                /*
                 * TODO: URL escaping; checking characters with javascript...
                 */
                httpdOutSpr(output,
                            "<input type=\"text\" name=\"v%d\" size=\"%d\""
                            "maxlength=\"%d\" value=\"%s\">\n",
                            i, len, len, str);

                if ( menu->m.numarray.sep[i] ) {
                    httpdOutSpr(output, "&nbsp;%c&nbsp;",
                                menu->m.numarray.sep[i]);
                } else {
                    httpdOutSpr(output, "&nbsp;");
                }
            }
            break;
        }

        case UI_MENU_TYPE_SCALE: {
            for ( i = menu->m.scale.min; i <= menu->m.scale.max; i++ ) {
                spr(str, sizeof(str), "%d", i);
                httpdOutPuts(output, "<br>\n");
                httpdOutSpr(output, "<input type=\"radio\" name=\"v\""
                                "value=\"%d\"%s>\n",
                                i,
                                i == menu->m.scale.curr ? " checked" : "");
                httpdOutSpr(output,
                                "<a href=\"/submit?v=%d&select=1\">%s</a>\n",
                                i, str);
                httpdOutPuts(output, "</li>\n");
            }
            break;
        }

        case UI_MENU_TYPE_DIALOG: {
            /*
             * TODO: if this a time dialog, inject some javascript
             * that reloads the page just after the timeout
             */
            char str[2*UI_MENU_MAXSTR]; /* text to display (may be long) */
            if ( menu->m.dialog.str[0] ) {
                sprcpy(str, sizeof(str), menu->m.dialog.str);
            } else {
                uiMenuTextGet(&menu->m.dialog.prompt, str, sizeof(str));
            }
            httpdOutPuts(output, str);
            break;
        }

        case UI_MENU_TYPE_CALENDAR: {
            //unsigned int year, month, sec, secNew, week;
            //int day, maxMDay, dow;
            //unsigned int currMDay, thisWeek;

            //httpdOutPuts(output, "<table border=\"0\">\n<tr>");
            ///*
            // * Place the week days
            // */
            //for ( i = 0 ; i < 7 ; i++ ) {
            //    httpdOutSpr(output, "<td align=\"right\">%s</td>",
            //           langGet(LANG_Calendar_WeekDay2 + i));
            //}
            //httpdOutPuts(output, "</tr>\n");

            ///*
            // * Now place the days of the month
            // */
            //dateTime_sec2calendar(menu->m.calendar.sec, &year, &month,
            //                      &currMDay, NULL, NULL, NULL);

            ///*
            // * Get the number of days in the month
            // */
            //sec     = dateTime_calendar2sec(year, month,   1, 0, 0, 0);
            //secNew  = dateTime_calendar2sec(year, month+1, 1, 0, 0, 0);
            //maxMDay = dateTime_diffDays(secNew, sec);
            //dow     = dateTime_sec2dow(sec);

            //thisWeek = (currMDay - 1 + dow) / 7;

            //menu->m.calendar.currMDay = currMDay;
            //menu->m.calendar.maxMDay = maxMDay;

            ///*
            // * Figure out the first display day
            // */
            //day = -dow + 1;

            //dow = 0;
            //for ( week = 0 ; day <= maxMDay ; day++ ) {
            //    if ( dow == 0 ) {
            //        httpdOutPuts(output, "<tr>");
            //    }
            //    httpdOutSpr(output, "<td align=\"right\">");
            //    if ( day >= 1 ) {
            //        spr(str, sizeof(str), "%d", day);
            //        httpdOutSpr(output, "<a href=\"/submit?day=%s\">", str);
            //        if ( day == currMDay ) {
            //            httpdOutSpr(output, "<b>%s</b>", str);
            //        } else {
            //            httpdOutSpr(output, "%s", str);
            //        }
            //        httpdOutSpr(output, "</a>\n");
            //    }
            //    httpdOutSpr(output, "</td>\n");
            //    dow++;
            //    if ( dow >= 7 ) {
            //        httpdOutPuts(output, "</tr>\n");
            //        dow = 0;
            //        week++;
            //    }
            //}
            //if ( dow > 0 ) {
            //    httpdOutPuts(output, "</tr>");
            //}
            //httpdOutPuts(output, "</table>\n");
            break;
        }

        case UI_MENU_TYPE_USER: {
            if ( menu->m.user.httpContent ) {
                menu->m.user.httpContent(output, url);
            }
            break;
        }

        case UI_MENU_TYPE_STATUS: {
            if ( menu->m.status.httpContent ) {
                menu->m.status.httpContent(output, url);
            }
            break;
        }

        default:
            debugf("uiMenuHttpContent: Unknown menu type %d\n", menu->type);
            break;
    }
    httpdOutPuts(output, "</form>\n");
}

/*
 * Should only be a handful of GET parameters
 */
#define   PARAM_MAX     10

/*
 * Handle the submit of a new value
 */
void uiMenuHttpSubmit(HttpdOutput *output, char *get)
{
    char *paramList[PARAM_MAX], *value;
    UIMenu *menu = &uiMenuS.MenuStack[uiMenuS.MenuTop];

    //printf("uiMenuHttpSubmit: got params %s menu->type=%d\n", get, menu->type);

    debugf("uiMenuHttpSubmit--->\n");
    httpdParseParams(get, paramList, PARAM_MAX);

    switch ( menu->type ) {
        case UI_MENU_TYPE_LIST: {

            if ( !(value = httpdParamLkup(paramList, "v")) ) {
                break;
            }
            if ( menu->m.list.nodeCntCB ) {
                menu->m.list.nodeCntCB(&menu->currNode,
                                       &menu->m.list.nNodes,
                                       menu->m.list.nodeCBArg);
            }
            int i = atoi(value);
            if ( i < 0 || i >= menu->m.list.nNodes ) {
                return;
            }
            if ( menu->currNode != i ) {
                menu->currNode = i;
                uiMenuRefresh();
            }
            if ( httpdParamLkup(paramList, "select") ) {
                if ( menu->m.list.nodeSelectCB ) {
                    menu->m.list.nodeSelectCB(i, menu->m.list.nodeCBArg);
                } else if ( menu->m.list.nodes[i].selectCB ) {
                    menu->m.list.nodes[i].selectCB(
                        menu->m.list.nodes[i].selectCBarg);
                }
            }
            break;
        }

        case UI_MENU_TYPE_NUM_ENTRY:
        case UI_MENU_TYPE_HEX_ENTRY:
        case UI_MENU_TYPE_TEXT_EX_ENTRY://2007/04/09    lizhen
        case UI_MENU_TYPE_TEXT_ENTRY:
		case UI_MENU_TYPE_PKEY_TEXT_ENTRY: // by Mike
		case UI_MENU_TYPE_TEXT_LIST_PBOOK_V2_ENTRY: //by Mike
			{
            if ( !(value = httpdParamLkup(paramList, "v")) ) {
                break;
            }
            /*
             * TODO: encoding/escaping etc
             */
            sprcpy(menu->m.text.str, sizeof(menu->m.text.str), value);
            break;
        }

        case UI_MENU_TYPE_NUMARRAY_ENTRY: {
            for ( int i = 0 ; i < menu->m.numarray.valN ; i++ ) {
                char var[20];

                spr(var, sizeof(var), "v%d", i);
                if ( (value = httpdParamLkup(paramList, var)) ) {
                    int val = atoi(value);
                    if ( val < menu->m.numarray.min[i] ) {
                        val = menu->m.numarray.min[i];
                    }
                    if ( val > menu->m.numarray.max[i] ) {
                        val = menu->m.numarray.max[i];
                    }
                    menu->m.numarray.val[i] = val;
                }
            }
            break;
        }

        case UI_MENU_TYPE_SCALE: {
            if ( !(value = httpdParamLkup(paramList, "v")) ) {
                break;
            }
            int val = atoi(value);
            if ( val < menu->m.scale.min ) val = menu->m.scale.min;
            if ( val > menu->m.scale.max ) val = menu->m.scale.max;
            menu->m.scale.curr = val;
            if ( menu->m.scale.valueCB ) {
                menu->m.scale.valueCB(
                    menu->m.scale.curr,
                    menu->m.scale.min,
                    menu->m.scale.max
                    );
            }
            Pw_WindowRepaint(&uiMenuS.MenuWin);
            break;
        }

        case UI_MENU_TYPE_DIALOG: {
            break;
        }

//        case UI_MENU_TYPE_CALENDAR: {
//            if ( (value = httpdParamLkup(paramList, "day")) ) {
//                int mDay = atoi(value);
//                if ( 1 <= mDay && mDay <= menu->m.calendar.maxMDay ) {
//                    menu->m.calendar.sec += (mDay - menu->m.calendar.currMDay)
//                                          * DATETIME_SECPERDAY;
//                    if ( menu->m.calendar.textCBargSec ) {
//                        menu->title.textCBarg = (void*)menu->m.calendar.sec;
//                    }
//                }
//            }
//            break;
//        }


        case UI_MENU_TYPE_USER: {
            if ( (value = httpdParamLkup(paramList, "v")) != NULL ) {
                int node = atoi(value);
                menu->currNode = node;
                if ( menu->m.user.httpNavGoTo ) {
                    /* Tell user code to switch but not descend... */
                    menu->m.user.httpNavGoTo(
                        node, uiMenuS.MenuTop, uiMenuS.MenuTop, 0);
                }
            }
            if ( menu->m.user.httpSubmit ) {
                menu->m.user.httpSubmit(output, paramList);
            }
            break;
        }

        case UI_MENU_TYPE_STATUS: {
            if ( (value = httpdParamLkup(paramList, "v")) != NULL ) {
                int node = atoi(value);
                menu->currNode = node;
                if ( menu->m.status.httpNavGoTo ) {
                    /* Tell user code to switch but not descend... */
                    menu->m.status.httpNavGoTo(
                        node, uiMenuS.MenuTop, uiMenuS.MenuTop, 0);
                }
            }
            if ( menu->m.status.httpSubmit ) {
                menu->m.status.httpSubmit(output, paramList);
            }
            break;
        }

        default:
            debugf("uiMenuHttpSubmit: Unknown menu type %d\n", menu->type);
            break;
    }

    /*
     * Check for soft key
     */
    if ( (value = httpdParamLkup(paramList, "soft")) ) {
        int softPos = atoi(value);      /* left to right position, 0==left */

        debugf("Got softPos %d\n", softPos);

        /* NOTE! softKey[] arrays are layed out from left to right always! */
        #if KEYPAD_SOFTKEY_NUM > 0
        if ( 0 <= softPos && softPos < KEYPAD_SOFTKEY_NUM ) {
            /*
             * Synthesize a PW win event and dispatch it
             */
            int keycode = 0;
            if (softPos == KEYPAD_POSITIVE_SOFTKEY_POSITION )
                keycode = KEYSYM_SOFT_POSITIVE;
            else
            if (softPos == KEYPAD_NEGATIVE_SOFTKEY_POSITION )
                keycode = KEYSYM_SOFT_NEGATIVE;
            else return;        // not supported for now...
            uiMenuKeyEvent(1, keycode, 0);
            //ev.data.type = Pw_KeyPressEventType;
            //ev.data.win  = &uiMenuS.MenuWin;
            //uiMenuWinEvents((Pw_Event*)&ev);
        }
        #endif
    }
}

#endif /* CFG_HTTPD_ENABLE */
